@@ -1,20 +1,23 @@
|
|
1 |
{
|
2 |
"projects": {
|
3 |
"dev": "dev-ion4fullpwa",
|
4 |
-
"prod": "ion4fullpwa",
|
5 |
-
"beginners": "ion5fullapp-beginners",
|
6 |
"basic": "basic-ionic-6-full-app"
|
7 |
},
|
8 |
"targets": {
|
9 |
"basic-ionic-6-full-app": {
|
10 |
"hosting": {
|
11 |
-
"
|
12 |
"basic-ionic-6-full-app"
|
13 |
-
]
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
16 |
]
|
17 |
}
|
18 |
}
|
19 |
-
}
|
|
|
20 |
}
|
1 |
{
|
2 |
"projects": {
|
3 |
"dev": "dev-ion4fullpwa",
|
|
|
|
|
4 |
"basic": "basic-ionic-6-full-app"
|
5 |
},
|
6 |
"targets": {
|
7 |
"basic-ionic-6-full-app": {
|
8 |
"hosting": {
|
9 |
+
"02-2023-release": [
|
10 |
"basic-ionic-6-full-app"
|
11 |
+
]
|
12 |
+
}
|
13 |
+
},
|
14 |
+
"dev-ion4fullpwa": {
|
15 |
+
"hosting": {
|
16 |
+
"02-2023-release": [
|
17 |
+
"dev-ionic-6-full-app"
|
18 |
]
|
19 |
}
|
20 |
}
|
21 |
+
},
|
22 |
+
"etags": {}
|
23 |
}
|
@@ -1,43 +1,22 @@
|
|
1 |
## Generate diff file
|
2 |
-
*BASIC
|
3 |
```bash
|
4 |
-
git diff
|
5 |
```
|
6 |
|
7 |
-
*BASIC vs
|
8 |
```bash
|
9 |
-
git diff
|
10 |
```
|
11 |
|
12 |
-
*BASIC vs
|
13 |
```bash
|
14 |
-
git diff
|
15 |
-
```
|
16 |
-
|
17 |
-
*Last BASIC update (**12-2021**) vs Current BASIC update (**06-2022**)*
|
18 |
-
```bash
|
19 |
-
git diff 12-2021_basic-update..basic-version --diff-filter=ADCMRT ':!package-lock.json' ':!www' ':!.gradle/*' ':!android' ':!ios' ':!*.diff' ':!*.png' ':!*.svg' > diff/last-basic-update-vs-current-basic_changelog.diff
|
20 |
-
```
|
21 |
-
|
22 |
-
*Last BASIC update (**12-2021**) vs Current BASIC update (**06-2022**) (**android config**)*
|
23 |
-
```bash
|
24 |
-
git diff 12-2021_basic-update..basic-version --diff-filter=ADCMRT -- android/ > diff/last-basic-update-vs-current-basic_android-changelog.diff
|
25 |
-
```
|
26 |
-
|
27 |
-
*Last BASIC update (**12-2021**) vs Current BASIC update (**06-2022**) (**ios config**)*
|
28 |
-
```bash
|
29 |
-
git diff 12-2021_basic-update..basic-version --diff-filter=ADCMRT -- ios/ > diff/last-basic-update-vs-current-basic_ios-changelog.diff
|
30 |
```
|
31 |
|
32 |
## Generate visual diff HTML file
|
33 |
*To generate a static HTML file with the diff*
|
34 |
```bash
|
35 |
-
diff2html --style side --file diff/previews/basic-vs-pro_changelog.html --input file -- diff/basic-vs-pro_changelog.diff
|
36 |
-
|
37 |
-
diff2html --style side --file diff/previews/basic-vs-pro_android-changelog.html --input file -- diff/basic-vs-pro_android-changelog.diff
|
38 |
-
|
39 |
-
diff2html --style side --file diff/previews/basic-vs-pro_ios-changelog.html --input file -- diff/basic-vs-pro_ios-changelog.diff
|
40 |
-
|
41 |
diff2html --style side --file diff/previews/last-basic-update-vs-current-basic_changelog.html --input file -- diff/last-basic-update-vs-current-basic_changelog.diff
|
42 |
|
43 |
diff2html --style side --file diff/previews/last-basic-update-vs-current-basic_android-changelog.html --input file -- diff/last-basic-update-vs-current-basic_android-changelog.diff
|
@@ -47,12 +26,6 @@ diff2html --style side --file diff/previews/last-basic-update-vs-current-basic_i
|
|
47 |
|
48 |
*To open the visual diff on the browser*
|
49 |
```bash
|
50 |
-
diff2html --style side --input file -- diff/basic-vs-pro_changelog.diff
|
51 |
-
|
52 |
-
diff2html --style side --input file -- diff/basic-vs-pro_android-changelog.diff
|
53 |
-
|
54 |
-
diff2html --style side --input file -- diff/basic-vs-pro_ios-changelog.diff
|
55 |
-
|
56 |
diff2html --style side --input file -- diff/last-basic-update-vs-current-basic_changelog.diff
|
57 |
|
58 |
diff2html --style side --input file -- diff/last-basic-update-vs-current-basic_android-changelog.diff
|
1 |
## Generate diff file
|
2 |
+
*Last BASIC update (**06-2022**) vs Current BASIC update (**02-2023**)*
|
3 |
```bash
|
4 |
+
git diff 06-2022_basic-update..basic-version --diff-filter=ADCMRT ':!package-lock.json' ':!www' ':!diff' ':!.gradle/*' ':!android' ':!ios' ':!*.diff' ':!*.png' ':!*.svg' > diff/last-basic-update-vs-current-basic_changelog.diff
|
5 |
```
|
6 |
|
7 |
+
*Last BASIC update (**06-2022**) vs Current BASIC update (**02-2023**) (**android config**)*
|
8 |
```bash
|
9 |
+
git diff 06-2022_basic-update..basic-version --diff-filter=ADCMRT -- android/ ':!*.svg' > diff/last-basic-update-vs-current-basic_android-changelog.diff
|
10 |
```
|
11 |
|
12 |
+
*Last BASIC update (**06-2022**) vs Current BASIC update (**02-2023**) (**ios config**)*
|
13 |
```bash
|
14 |
+
git diff 06-2022_basic-update..basic-version --diff-filter=ADCMRT -- ios/ > diff/last-basic-update-vs-current-basic_ios-changelog.diff
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
```
|
16 |
|
17 |
## Generate visual diff HTML file
|
18 |
*To generate a static HTML file with the diff*
|
19 |
```bash
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
diff2html --style side --file diff/previews/last-basic-update-vs-current-basic_changelog.html --input file -- diff/last-basic-update-vs-current-basic_changelog.diff
|
21 |
|
22 |
diff2html --style side --file diff/previews/last-basic-update-vs-current-basic_android-changelog.html --input file -- diff/last-basic-update-vs-current-basic_android-changelog.diff
|
26 |
|
27 |
*To open the visual diff on the browser*
|
28 |
```bash
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
diff2html --style side --input file -- diff/last-basic-update-vs-current-basic_changelog.diff
|
30 |
|
31 |
diff2html --style side --input file -- diff/last-basic-update-vs-current-basic_android-changelog.diff
|
@@ -1,23 +1,24 @@
|
|
1 |
-
#
|
|
|
|
|
2 |
Before deploying the app to Firebase Hosting, run `ionic build --prod`
|
3 |
|
4 |
-
|
5 |
For managing one site across different environments, we recommend multiple projects for promoting best practices of each environment having its own set of Firebase resources.
|
6 |
|
7 |
For example for this repo we will have two firebase projects:
|
8 |
- dev-ion4fullpwa
|
9 |
-
-
|
10 |
|
11 |
-
|
12 |
Before deploying to Firebase Hosting make sure you are using the correct alias (dev, prod)
|
13 |
`firebase use` will list all the alias available
|
14 |
```
|
15 |
* dev (dev-ion4fullpwa)
|
16 |
-
|
17 |
-
pro (pro-ion4fullpwa)
|
18 |
```
|
19 |
|
20 |
-
|
21 |
If you don't see these alias (dev, prod), you should create them
|
22 |
`firebase use --add`
|
23 |
```
|
@@ -25,7 +26,7 @@ If you don't see these alias (dev, prod), you should create them
|
|
25 |
? What alias do you want to use for this project? (e.g. staging) prod
|
26 |
```
|
27 |
|
28 |
-
|
29 |
`firebase use dev`
|
30 |
|
31 |
You can also use the `-P` flag to specify an alias like this:
|
@@ -35,15 +36,15 @@ firebase deploy --only hosting -P dev
|
|
35 |
|
36 |
This will deploy to the `dev` alias/environment
|
37 |
|
38 |
-
|
39 |
For more info see: https://firebase.google.com/docs/hosting/deploying
|
40 |
`firebase serve --only hosting`
|
41 |
|
42 |
---
|
43 |
|
44 |
-
|
45 |
|
46 |
-
|
47 |
- TARGET_NAME = 12-2021-release
|
48 |
- RESOURCE_IDENTIFIER (the SITE_ID) = basic-ionic-5-full-app
|
49 |
``` bash
|
@@ -57,7 +58,7 @@ firebase target:apply hosting 06-2022-release basic-ionic-6-full-app -P basic
|
|
57 |
```
|
58 |
|
59 |
|
60 |
-
|
61 |
Don't forget to configure this before deploying to the new target
|
62 |
|
63 |
|
@@ -67,8 +68,85 @@ firebase deploy --only hosting:06-2022-release -P basic
|
|
67 |
```
|
68 |
|
69 |
|
70 |
-
|
71 |
-
|
|
|
|
|
|
|
72 |
``` bash
|
73 |
-
firebase hosting:channel:deploy
|
74 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Firebase instructions
|
2 |
+
|
3 |
+
### Prepare assets to deploy
|
4 |
Before deploying the app to Firebase Hosting, run `ionic build --prod`
|
5 |
|
6 |
+
### Multiple Environments with Firebase
|
7 |
For managing one site across different environments, we recommend multiple projects for promoting best practices of each environment having its own set of Firebase resources.
|
8 |
|
9 |
For example for this repo we will have two firebase projects:
|
10 |
- dev-ion4fullpwa
|
11 |
+
- basic-ionic-6-full-app
|
12 |
|
13 |
+
### Check available alias
|
14 |
Before deploying to Firebase Hosting make sure you are using the correct alias (dev, prod)
|
15 |
`firebase use` will list all the alias available
|
16 |
```
|
17 |
* dev (dev-ion4fullpwa)
|
18 |
+
basic (basic-ionic-6-full-app)
|
|
|
19 |
```
|
20 |
|
21 |
+
### Create alias
|
22 |
If you don't see these alias (dev, prod), you should create them
|
23 |
`firebase use --add`
|
24 |
```
|
26 |
? What alias do you want to use for this project? (e.g. staging) prod
|
27 |
```
|
28 |
|
29 |
+
### Select alias (switching environments)
|
30 |
`firebase use dev`
|
31 |
|
32 |
You can also use the `-P` flag to specify an alias like this:
|
36 |
|
37 |
This will deploy to the `dev` alias/environment
|
38 |
|
39 |
+
### Serve and test your Firebase project locally
|
40 |
For more info see: https://firebase.google.com/docs/hosting/deploying
|
41 |
`firebase serve --only hosting`
|
42 |
|
43 |
---
|
44 |
|
45 |
+
## [Advanced uses](https://firebase.google.com/docs/cli/targets#deploy-target-commands)
|
46 |
|
47 |
+
### [Create target](https://firebase.google.com/docs/cli/targets#set-up-deploy-target-hosting)
|
48 |
- TARGET_NAME = 12-2021-release
|
49 |
- RESOURCE_IDENTIFIER (the SITE_ID) = basic-ionic-5-full-app
|
50 |
``` bash
|
58 |
```
|
59 |
|
60 |
|
61 |
+
### [Configure your `firebase.json` file to use deploy targets](https://firebase.google.com/docs/cli/targets#configure_your_firebasejson_file_to_use_deploy_targets)
|
62 |
Don't forget to configure this before deploying to the new target
|
63 |
|
64 |
|
68 |
```
|
69 |
|
70 |
|
71 |
+
### Create preview channel
|
72 |
+
> Preview channels don't have Firebase auth configured automatically `Unable to add channel domain to Firebase Auth`
|
73 |
+
- CHANNEL_ID = 6-0-0
|
74 |
+
- TARGET_NAME = 02-2023-release
|
75 |
+
- PROJECT_ALIAS = basic
|
76 |
``` bash
|
77 |
+
firebase hosting:channel:deploy 6-0-0 --only 02-2023-release -P basic
|
78 |
```
|
79 |
+
|
80 |
+
|
81 |
+
---
|
82 |
+
|
83 |
+
# Update release process
|
84 |
+
|
85 |
+
## DEV
|
86 |
+
First test the changes on the dev site (dev-ionic-6-full-app)
|
87 |
+
|
88 |
+
|
89 |
+
### 1 - Create new target
|
90 |
+
> This updates the `.firebaserc` file
|
91 |
+
|
92 |
+
- TARGET_NAME = 02-2023-release
|
93 |
+
- RESOURCE_IDENTIFIER (the SITE_ID) = dev-ionic-6-full-app
|
94 |
+
- PROJECT_ALIAS = dev
|
95 |
+
``` bash
|
96 |
+
firebase target:apply hosting 02-2023-release dev-ionic-6-full-app -P dev
|
97 |
+
```
|
98 |
+
|
99 |
+
|
100 |
+
### 2 - Update firebase.json
|
101 |
+
> Manually update the file and add the new target (`02-2023-release`) config (just copy and paste from the previous one)
|
102 |
+
|
103 |
+
|
104 |
+
### [OPTIONAL] - Create preview channel for the latest update you want to test live
|
105 |
+
> Preview channels don't have Firebase auth configured automatically `Unable to add channel domain to Firebase Auth`
|
106 |
+
- CHANNEL_ID = 6-0-0
|
107 |
+
- TARGET_NAME = 02-2023-release
|
108 |
+
- PROJECT_ALIAS = dev
|
109 |
+
``` bash
|
110 |
+
firebase hosting:channel:deploy 6-0-0 --only 02-2023-release -P dev
|
111 |
+
```
|
112 |
+
|
113 |
+
|
114 |
+
### 3 - Deploy and test new version of the site
|
115 |
+
> Deploy the new version of the site
|
116 |
+
- TARGET_NAME = 02-2023-release
|
117 |
+
- PROJECT_ALIAS = dev
|
118 |
+
``` bash
|
119 |
+
firebase deploy --only hosting:02-2023-release -P dev
|
120 |
+
```
|
121 |
+
|
122 |
+
> Go to the preview channel and test it
|
123 |
+
[https://dev-ionic-6-full-app.web.app]
|
124 |
+
|
125 |
+
|
126 |
+
---
|
127 |
+
|
128 |
+
## LIVE (prod)
|
129 |
+
After you test the new version of the site, you can deploy to the production site (basic-ionic-6-full-app)
|
130 |
+
|
131 |
+
|
132 |
+
### 1 - Create new target
|
133 |
+
> This updates the `.firebaserc` file
|
134 |
+
|
135 |
+
- TARGET_NAME = 02-2023-release
|
136 |
+
- RESOURCE_IDENTIFIER (the SITE_ID) = basic-ionic-6-full-app
|
137 |
+
- PROJECT_ALIAS = basic
|
138 |
+
``` bash
|
139 |
+
firebase target:apply hosting 02-2023-release basic-ionic-6-full-app -P basic
|
140 |
+
```
|
141 |
+
|
142 |
+
|
143 |
+
### 2 - Deploy and test new version of the site
|
144 |
+
> Deploy the new version of the site
|
145 |
+
- TARGET_NAME = 02-2023-release
|
146 |
+
- PROJECT_ALIAS = basic
|
147 |
+
``` bash
|
148 |
+
firebase deploy --only hosting:02-2023-release -P basic
|
149 |
+
```
|
150 |
+
|
151 |
+
> Go to the preview channel and test it
|
152 |
+
[https://basic-ionic-6-full-app.web.app]
|
@@ -16,7 +16,6 @@ Run `ionic build` or `ionic build --prod` to build the project
|
|
16 |
Run `ionic serve` to start a live-reload dev server
|
17 |
|
18 |
### To test the app as a Native App
|
19 |
-
|
20 |
This project uses [Capacitor](https://capacitor.ionicframework.com/docs/) (spiritual successor to Cordova).
|
21 |
|
22 |
[Read this post](https://ionicthemes.com/tutorials/about/native-cross-platform-web-apps-with-ionic-capacitor) to get an introduction about Capacitor and learn the main differences between Capacitor and Cordova.
|
@@ -29,14 +28,38 @@ The Capacitor workflow involves a few consistent tasks:
|
|
29 |
- [Open your Native IDE](https://capacitor.ionicframework.com/docs/basics/workflow/#3-open-your-native-ide)
|
30 |
- [Periodic Maintenance](https://capacitor.ionicframework.com/docs/basics/workflow/#4-periodic-maintenance)
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
#### iOS Platform
|
33 |
-
This app has an
|
34 |
Read how to [build this app for iOS](https://capacitor.ionicframework.com/docs/basics/building-your-app#ios).
|
35 |
|
36 |
#### Android Platform
|
37 |
This app has an android folder which contains the Android native app.
|
38 |
Read how to [build this app for Android](https://capacitor.ionicframework.com/docs/basics/building-your-app#android).
|
39 |
|
|
|
|
|
40 |
|
41 |
## Support
|
42 |
Drop us a line to contact@ionicthemes.com
|
16 |
Run `ionic serve` to start a live-reload dev server
|
17 |
|
18 |
### To test the app as a Native App
|
|
|
19 |
This project uses [Capacitor](https://capacitor.ionicframework.com/docs/) (spiritual successor to Cordova).
|
20 |
|
21 |
[Read this post](https://ionicthemes.com/tutorials/about/native-cross-platform-web-apps-with-ionic-capacitor) to get an introduction about Capacitor and learn the main differences between Capacitor and Cordova.
|
28 |
- [Open your Native IDE](https://capacitor.ionicframework.com/docs/basics/workflow/#3-open-your-native-ide)
|
29 |
- [Periodic Maintenance](https://capacitor.ionicframework.com/docs/basics/workflow/#4-periodic-maintenance)
|
30 |
|
31 |
+
#### Building your web code
|
32 |
+
> Once you are ready to test your web app on a mobile device, you'll need to build your web app for distribution.
|
33 |
+
|
34 |
+
Run `ionic build`
|
35 |
+
|
36 |
+
#### Syncing your web code to your Capacitor project
|
37 |
+
> Once your web code has been built for distribution, you will need to push your web code to the web native Capacitor application.
|
38 |
+
|
39 |
+
Run `npx cap sync`
|
40 |
+
|
41 |
+
> Running `npx cap sync` will copy over your already built web bundle to both your Android and iOS projects as well as update the native dependencies that Capacitor uses.
|
42 |
+
|
43 |
+
#### Testing your Capacitor app
|
44 |
+
> Once you've synced over your web bundle to your native project, it is time to test your application on a mobile device.
|
45 |
+
|
46 |
+
Run `npx cap run ios` and `npx cap run android`
|
47 |
+
|
48 |
+
##### Open your Native IDE
|
49 |
+
> If you'd like more control over your native project you can quickly open the native IDEs using the Capacitor CLI.
|
50 |
+
|
51 |
+
Run `npx cap open ios` and `npx cap open android`
|
52 |
+
|
53 |
#### iOS Platform
|
54 |
+
This app has an iOS folder which contains the iOS native app.
|
55 |
Read how to [build this app for iOS](https://capacitor.ionicframework.com/docs/basics/building-your-app#ios).
|
56 |
|
57 |
#### Android Platform
|
58 |
This app has an android folder which contains the Android native app.
|
59 |
Read how to [build this app for Android](https://capacitor.ionicframework.com/docs/basics/building-your-app#android).
|
60 |
|
61 |
+
### Want to use Cordova?
|
62 |
+
The BASIC version of the template uses Capacitor instead of Cordova, however, if you are not yet ready to use it, in the following link we show you how to remove Capacitor and add Cordova to this project: https://ionic-4-full-starter-app-docs.ionicthemes.com/capacitor#steps-to-remove-capacitor-and-add-cordova
|
63 |
|
64 |
## Support
|
65 |
Drop us a line to contact@ionicthemes.com
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Update process
|
2 |
+
|
3 |
+
## Capacitor 4
|
4 |
+
|
5 |
+
### Update from Capacitor 3 to Capacitor 4
|
6 |
+
> Install the latest version of the Capacitor CLI to your project
|
7 |
+
``` bash
|
8 |
+
npm i -D @capacitor/cli@latest
|
9 |
+
```
|
10 |
+
|
11 |
+
>Use the CLI to handle the migration for you
|
12 |
+
``` bash
|
13 |
+
npx cap migrate
|
14 |
+
```
|
15 |
+
|
16 |
+
The migration should be smooth, and all the steps required both for iOS and Android are handled by the CLI. However, you can check both iOS and Android specific migration steps here:
|
17 |
+
- [iOS update](https://capacitorjs.com/docs/updating/4-0#ios)
|
18 |
+
- [Android update](https://capacitorjs.com/docs/updating/4-0#android)
|
19 |
+
|
20 |
+
|
21 |
+
---
|
22 |
+
|
23 |
+
|
24 |
+
### Update Capacitor plugins
|
25 |
+
> All Capacitor official plugins were updated when running the migration tool.
|
26 |
+
> The Capacitor community plugins need to be updated manually
|
27 |
+
``` bash
|
28 |
+
npm install --save @capacitor-firebase/authentication@1.3.0
|
29 |
+
```
|
30 |
+
|
31 |
+
> Make sure you run this command after updating the Capacitor plugins
|
32 |
+
``` bash
|
33 |
+
npx cap sync
|
34 |
+
```
|
35 |
+
|
36 |
+
|
37 |
+
---
|
38 |
+
|
39 |
+
|
40 |
+
## Ionic 6.5.0
|
41 |
+
|
42 |
+
### Update to Ionic 6.5.0
|
43 |
+
``` bash
|
44 |
+
ng update @ionic/angular@latest --create-commits
|
45 |
+
```
|
46 |
+
|
47 |
+
### Update Ionic dev dependencies
|
48 |
+
``` bash
|
49 |
+
ng update @ionic/angular-toolkit @ionic/cli --create-commits
|
50 |
+
```
|
51 |
+
|
52 |
+
|
53 |
+
--
|
54 |
+
|
55 |
+
|
56 |
+
## Angular 15
|
57 |
+
|
58 |
+
> Make sure the Angular CLI is up-to-date
|
59 |
+
``` bash
|
60 |
+
npm install -g @angular/cli
|
61 |
+
```
|
62 |
+
|
63 |
+
### Update from Angular 13 to Angular 14
|
64 |
+
> We should update first to Angular 14 and then to Angular 15
|
65 |
+
``` bash
|
66 |
+
ng update @angular-eslint/schematics@14 @angular/core@14 @angular/cli@14 --create-commits
|
67 |
+
```
|
68 |
+
|
69 |
+
### Update from Angular 14 to Angular 15
|
70 |
+
> Then we can update Angular 15
|
71 |
+
``` bash
|
72 |
+
ng update @angular/fire @angular-eslint/schematics@15 @angular/core@15 @angular/cli@15 --create-commits
|
73 |
+
```
|
74 |
+
|
75 |
+
|
76 |
+
## Other dependencies
|
77 |
+
> First check outdated dependencies by running this command
|
78 |
+
``` bash
|
79 |
+
npm outdated --depth=0 --long
|
80 |
+
```
|
81 |
+
|
82 |
+
### Update dependencies
|
83 |
+
``` bash
|
84 |
+
npm install --save firebase
|
85 |
+
npm install --save core-js
|
86 |
+
npm install --save dayjs
|
87 |
+
npm install --save google-libphonenumber
|
88 |
+
npm install --save rxjs@7.8.0
|
89 |
+
npm install --save swiper
|
90 |
+
npm install --save tslib
|
91 |
+
npm install --save zone.js
|
92 |
+
```
|
93 |
+
|
94 |
+
### Update dev dependencies
|
95 |
+
``` bash
|
96 |
+
npm install --save-dev @commitlint/cli @commitlint/config-angular
|
97 |
+
npm install --save-dev @types/node
|
98 |
+
npm install --save-dev @webcomponents/webcomponentsjs
|
99 |
+
npm install --save-dev ts-node
|
100 |
+
```
|
101 |
+
|
102 |
+
|
103 |
+
# 2023-02-01 Updated dependencies list
|
104 |
+
> Check by running this command:
|
105 |
+
``` bash
|
106 |
+
npm list --depth=0
|
107 |
+
```
|
108 |
+
|
109 |
+
- @angular-devkit/architect@0.1501.2
|
110 |
+
- @angular-devkit/build-angular@15.1.2
|
111 |
+
- @angular-devkit/core@15.1.2
|
112 |
+
- @angular-devkit/schematics@15.1.2
|
113 |
+
- @angular-eslint/builder@15.2.0
|
114 |
+
- @angular-eslint/eslint-plugin@15.2.0
|
115 |
+
- @angular-eslint/eslint-plugin-template@15.2.0
|
116 |
+
- @angular-eslint/schematics@15.2.0
|
117 |
+
- @angular-eslint/template-parser@15.2.0
|
118 |
+
- @angular/animations@15.1.1
|
119 |
+
- @angular/cli@15.1.2
|
120 |
+
- @angular/common@15.1.1
|
121 |
+
- @angular/compiler@15.1.1
|
122 |
+
- @angular/compiler-cli@15.1.1
|
123 |
+
- @angular/core@15.1.1
|
124 |
+
- @angular/fire@7.5.0
|
125 |
+
- @angular/forms@15.1.1
|
126 |
+
- @angular/language-service@15.1.1
|
127 |
+
- @angular/platform-browser@15.1.1
|
128 |
+
- @angular/platform-browser-dynamic@15.1.1
|
129 |
+
- @angular/router@15.1.1
|
130 |
+
- @angular/service-worker@15.1.1
|
131 |
+
- @capacitor-firebase/authentication@1.3.0
|
132 |
+
- @capacitor/android@4.6.2
|
133 |
+
- @capacitor/app@4.1.1
|
134 |
+
- @capacitor/cli@4.6.2
|
135 |
+
- @capacitor/core@4.6.2
|
136 |
+
- @capacitor/geolocation@4.1.0
|
137 |
+
- @capacitor/haptics@4.1.0
|
138 |
+
- @capacitor/ios@4.6.2
|
139 |
+
- @capacitor/keyboard@4.1.1
|
140 |
+
- @capacitor/preferences@4.0.2
|
141 |
+
- @capacitor/share@4.1.0
|
142 |
+
- @capacitor/splash-screen@4.1.3
|
143 |
+
- @capacitor/status-bar@4.1.1
|
144 |
+
- @commitlint/cli@17.4.2
|
145 |
+
- @commitlint/config-angular@17.4.2
|
146 |
+
- @ionic/angular@6.5.0
|
147 |
+
- @ionic/angular-toolkit@7.0.0
|
148 |
+
- @ionic/cli@6.20.8
|
149 |
+
- @types/core-js@2.5.5
|
150 |
+
- @types/node@17.0.45
|
151 |
+
- @typescript-eslint/eslint-plugin@5.49.0
|
152 |
+
- @typescript-eslint/parser@5.49.0
|
153 |
+
- @webcomponents/webcomponentsjs@2.7.0
|
154 |
+
- angular-pipes@10.0.0
|
155 |
+
- cordova-res@0.15.4
|
156 |
+
- core-js@2.6.12
|
157 |
+
- dayjs@1.11.7
|
158 |
+
- eslint@8.32.0
|
159 |
+
- firebase@9.16.0
|
160 |
+
- google-libphonenumber@3.2.31
|
161 |
+
- husky@4.3.8
|
162 |
+
- rxjs@7.8.0
|
163 |
+
- swiper@8.4.6
|
164 |
+
- ts-node@8.10.2
|
165 |
+
- tslib@2.4.1
|
166 |
+
- typescript@4.8.4
|
167 |
+
- zone.js@0.11.8
|
168 |
+
|
169 |
+
|
170 |
+
---
|
171 |
+
|
172 |
+
|
173 |
+
# Tags
|
174 |
+
To create an annotated tag, enter the following:
|
175 |
+
``` bash
|
176 |
+
git tag -a 11-2022_elite-release -m 'the initial ELITE release'
|
177 |
+
```
|
178 |
+
Then run:
|
179 |
+
``` bash
|
180 |
+
git push origin --tags
|
181 |
+
```
|
182 |
+
|
183 |
+
|
184 |
+
---
|
185 |
+
|
186 |
+
|
187 |
+
# Miscellaneous
|
188 |
+
> When updating an existing project you may want to get a specific file from another branch
|
189 |
+
|
190 |
+
### Cherry-pick files from another git branch
|
191 |
+
> Get just one file from another branch
|
192 |
+
``` bash
|
193 |
+
git checkout pro-version -- src/global.scss
|
194 |
+
git checkout pro-version -- src/app/firebase/auth/firebase-auth.helper.ts
|
195 |
+
git checkout pro-version -- src/app/firebase/auth/firebase-auth.module.ts
|
196 |
+
```
|
@@ -1,7 +1,6 @@
|
|
1 |
{
|
2 |
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
|
3 |
"version": 1,
|
4 |
-
"defaultProject": "app",
|
5 |
"newProjectRoot": "projects",
|
6 |
"projects": {
|
7 |
"app": {
|
@@ -127,7 +126,10 @@
|
|
127 |
}
|
128 |
},
|
129 |
"cli": {
|
130 |
-
"
|
|
|
|
|
|
|
131 |
},
|
132 |
"schematics": {
|
133 |
"@ionic/angular-toolkit:component": {
|
@@ -135,6 +137,12 @@
|
|
135 |
},
|
136 |
"@ionic/angular-toolkit:page": {
|
137 |
"styleext": "scss"
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
}
|
139 |
}
|
140 |
-
}
|
1 |
{
|
2 |
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
|
3 |
"version": 1,
|
|
|
4 |
"newProjectRoot": "projects",
|
5 |
"projects": {
|
6 |
"app": {
|
126 |
}
|
127 |
},
|
128 |
"cli": {
|
129 |
+
"schematicCollections": [
|
130 |
+
"@ionic/angular-toolkit",
|
131 |
+
"@angular-eslint/schematics"
|
132 |
+
]
|
133 |
},
|
134 |
"schematics": {
|
135 |
"@ionic/angular-toolkit:component": {
|
137 |
},
|
138 |
"@ionic/angular-toolkit:page": {
|
139 |
"styleext": "scss"
|
140 |
+
},
|
141 |
+
"@angular-eslint/schematics:application": {
|
142 |
+
"setParserOptionsProject": true
|
143 |
+
},
|
144 |
+
"@angular-eslint/schematics:library": {
|
145 |
+
"setParserOptionsProject": true
|
146 |
}
|
147 |
}
|
148 |
+
}
|
@@ -7,7 +7,7 @@ const config: CapacitorConfig = {
|
|
7 |
bundledWebRuntime: false,
|
8 |
plugins: {
|
9 |
SplashScreen: {
|
10 |
-
launchAutoHide: false
|
11 |
},
|
12 |
FirebaseAuthentication: {
|
13 |
skipNativeAuth: false,
|
7 |
bundledWebRuntime: false,
|
8 |
plugins: {
|
9 |
SplashScreen: {
|
10 |
+
launchAutoHide: false
|
11 |
},
|
12 |
FirebaseAuthentication: {
|
13 |
skipNativeAuth: false,
|
@@ -1,5 +1,47 @@
|
|
1 |
{
|
2 |
"hosting": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
{
|
4 |
"target": "06-2022-release",
|
5 |
"public": "www",
|
1 |
{
|
2 |
"hosting": [
|
3 |
+
{
|
4 |
+
"target": "02-2023-release",
|
5 |
+
"public": "www",
|
6 |
+
"ignore": [
|
7 |
+
"firebase.json",
|
8 |
+
"**/.*",
|
9 |
+
"**/node_modules/**"
|
10 |
+
],
|
11 |
+
"rewrites": [ {
|
12 |
+
"source": "**",
|
13 |
+
"destination": "/index.html"
|
14 |
+
} ],
|
15 |
+
"headers": [
|
16 |
+
{
|
17 |
+
"source": "**",
|
18 |
+
"headers": [
|
19 |
+
{
|
20 |
+
"key": "Cache-Control",
|
21 |
+
"value": "no-cache, no-store, must-revalidate"
|
22 |
+
}
|
23 |
+
]
|
24 |
+
},
|
25 |
+
{
|
26 |
+
"source": "**/*.@(jpg|jpeg|gif|png|svg|webp|js|css|eot|otf|ttf|ttc|woff|font.css)",
|
27 |
+
"headers": [
|
28 |
+
{
|
29 |
+
"key": "Cache-Control",
|
30 |
+
"value": "no-cache"
|
31 |
+
}
|
32 |
+
]
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"source": "ngsw-worker.js",
|
36 |
+
"headers": [
|
37 |
+
{
|
38 |
+
"key": "Cache-Control",
|
39 |
+
"value": "no-cache"
|
40 |
+
}
|
41 |
+
]
|
42 |
+
}
|
43 |
+
]
|
44 |
+
},
|
45 |
{
|
46 |
"target": "06-2022-release",
|
47 |
"public": "www",
|
@@ -1,7 +1,7 @@
|
|
1 |
{
|
2 |
"name": "IonicFullApp-BASIC",
|
3 |
"description": "The most advanced and complete Mobile & PWA Ionic starter app template",
|
4 |
-
"version": "
|
5 |
"author": "IonicThemes",
|
6 |
"contributors": [
|
7 |
"Dayana <dayana@ionicthemes.com>",
|
@@ -16,66 +16,66 @@
|
|
16 |
"lint": "ng lint"
|
17 |
},
|
18 |
"dependencies": {
|
19 |
-
"@angular/animations": "^
|
20 |
-
"@angular/common": "^
|
21 |
-
"@angular/core": "^
|
22 |
-
"@angular/fire": "^7.
|
23 |
-
"@angular/forms": "^
|
24 |
-
"@angular/platform-browser": "^
|
25 |
-
"@angular/platform-browser-dynamic": "^
|
26 |
-
"@angular/router": "^
|
27 |
-
"@angular/service-worker": "^
|
28 |
-
"@capacitor-firebase/authentication": "^
|
29 |
-
"@capacitor/android": "^
|
30 |
-
"@capacitor/app": "^
|
31 |
-
"@capacitor/core": "^
|
32 |
-
"@capacitor/geolocation": "^
|
33 |
-
"@capacitor/haptics": "^
|
34 |
-
"@capacitor/ios": "^
|
35 |
-
"@capacitor/keyboard": "^
|
36 |
-
"@capacitor/
|
37 |
-
"@capacitor/
|
38 |
-
"@capacitor/
|
39 |
-
"@capacitor/
|
40 |
-
"@ionic/angular": "^6.
|
41 |
"@types/core-js": "^2.5.5",
|
42 |
"angular-pipes": "^10.0.0",
|
43 |
-
"core-js": "^2.6.
|
44 |
-
"dayjs": "^1.11.
|
45 |
-
"firebase": "^9.
|
46 |
-
"google-libphonenumber": "^3.2.
|
47 |
-
"rxjs": "
|
48 |
-
"swiper": "^8.
|
49 |
-
"tslib": "^2.
|
50 |
-
"zone.js": "
|
51 |
},
|
52 |
"devDependencies": {
|
53 |
-
"@angular-devkit/architect": "^0.
|
54 |
-
"@angular-devkit/build-angular": "^
|
55 |
-
"@angular-devkit/core": "^
|
56 |
-
"@angular-devkit/schematics": "^
|
57 |
-
"@angular-eslint/builder": "
|
58 |
-
"@angular-eslint/eslint-plugin": "
|
59 |
-
"@angular-eslint/eslint-plugin-template": "
|
60 |
-
"@angular-eslint/schematics": "
|
61 |
-
"@angular-eslint/template-parser": "
|
62 |
-
"@angular/cli": "^
|
63 |
-
"@angular/compiler": "^
|
64 |
-
"@angular/compiler-cli": "^
|
65 |
-
"@angular/language-service": "
|
66 |
-
"@capacitor/cli": "^
|
67 |
-
"@commitlint/cli": "^17.
|
68 |
-
"@commitlint/config-angular": "^17.
|
69 |
-
"@ionic/angular-toolkit": "^
|
70 |
-
"@ionic/cli": "6.
|
71 |
-
"@types/node": "^17.0.
|
72 |
-
"@typescript-eslint/eslint-plugin": "^5.
|
73 |
-
"@typescript-eslint/parser": "^5.
|
74 |
-
"@webcomponents/webcomponentsjs": "^2.
|
75 |
"cordova-res": "0.15.4",
|
76 |
-
"eslint": "^8.
|
77 |
"husky": "^4.3.0",
|
78 |
-
"ts-node": "^8.10.
|
79 |
-
"typescript": "~4.
|
80 |
}
|
81 |
}
|
1 |
{
|
2 |
"name": "IonicFullApp-BASIC",
|
3 |
"description": "The most advanced and complete Mobile & PWA Ionic starter app template",
|
4 |
+
"version": "6.0.0",
|
5 |
"author": "IonicThemes",
|
6 |
"contributors": [
|
7 |
"Dayana <dayana@ionicthemes.com>",
|
16 |
"lint": "ng lint"
|
17 |
},
|
18 |
"dependencies": {
|
19 |
+
"@angular/animations": "^15.1.1",
|
20 |
+
"@angular/common": "^15.1.1",
|
21 |
+
"@angular/core": "^15.1.1",
|
22 |
+
"@angular/fire": "^7.5.0",
|
23 |
+
"@angular/forms": "^15.1.1",
|
24 |
+
"@angular/platform-browser": "^15.1.1",
|
25 |
+
"@angular/platform-browser-dynamic": "^15.1.1",
|
26 |
+
"@angular/router": "^15.1.1",
|
27 |
+
"@angular/service-worker": "^15.1.1",
|
28 |
+
"@capacitor-firebase/authentication": "^1.3.0",
|
29 |
+
"@capacitor/android": "^4.0.0",
|
30 |
+
"@capacitor/app": "^4.0.0",
|
31 |
+
"@capacitor/core": "^4.0.0",
|
32 |
+
"@capacitor/geolocation": "^4.0.0",
|
33 |
+
"@capacitor/haptics": "^4.0.0",
|
34 |
+
"@capacitor/ios": "^4.0.0",
|
35 |
+
"@capacitor/keyboard": "^4.0.0",
|
36 |
+
"@capacitor/preferences": "^4.0.2",
|
37 |
+
"@capacitor/share": "^4.0.0",
|
38 |
+
"@capacitor/splash-screen": "^4.0.0",
|
39 |
+
"@capacitor/status-bar": "^4.0.0",
|
40 |
+
"@ionic/angular": "^6.5.0",
|
41 |
"@types/core-js": "^2.5.5",
|
42 |
"angular-pipes": "^10.0.0",
|
43 |
+
"core-js": "^2.6.12",
|
44 |
+
"dayjs": "^1.11.7",
|
45 |
+
"firebase": "^9.16.0",
|
46 |
+
"google-libphonenumber": "^3.2.31",
|
47 |
+
"rxjs": "^7.8.0",
|
48 |
+
"swiper": "^8.4.6",
|
49 |
+
"tslib": "^2.4.1",
|
50 |
+
"zone.js": "^0.11.8"
|
51 |
},
|
52 |
"devDependencies": {
|
53 |
+
"@angular-devkit/architect": "^0.1501.2",
|
54 |
+
"@angular-devkit/build-angular": "^15.1.2",
|
55 |
+
"@angular-devkit/core": "^15.1.2",
|
56 |
+
"@angular-devkit/schematics": "^15.1.2",
|
57 |
+
"@angular-eslint/builder": "15.2.0",
|
58 |
+
"@angular-eslint/eslint-plugin": "15.2.0",
|
59 |
+
"@angular-eslint/eslint-plugin-template": "15.2.0",
|
60 |
+
"@angular-eslint/schematics": "15.2.0",
|
61 |
+
"@angular-eslint/template-parser": "15.2.0",
|
62 |
+
"@angular/cli": "^15.1.2",
|
63 |
+
"@angular/compiler": "^15.1.1",
|
64 |
+
"@angular/compiler-cli": "^15.1.1",
|
65 |
+
"@angular/language-service": "^15.1.1",
|
66 |
+
"@capacitor/cli": "^4.0.0",
|
67 |
+
"@commitlint/cli": "^17.4.2",
|
68 |
+
"@commitlint/config-angular": "^17.4.2",
|
69 |
+
"@ionic/angular-toolkit": "^7.0.0",
|
70 |
+
"@ionic/cli": "6.20.8",
|
71 |
+
"@types/node": "^17.0.45",
|
72 |
+
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
73 |
+
"@typescript-eslint/parser": "^5.43.0",
|
74 |
+
"@webcomponents/webcomponentsjs": "^2.7.0",
|
75 |
"cordova-res": "0.15.4",
|
76 |
+
"eslint": "^8.28.0",
|
77 |
"husky": "^4.3.0",
|
78 |
+
"ts-node": "^8.10.2",
|
79 |
+
"typescript": "~4.8.4"
|
80 |
}
|
81 |
}
|
@@ -77,10 +77,9 @@ const routes: Routes = [
|
|
77 |
@NgModule({
|
78 |
imports: [
|
79 |
RouterModule.forRoot(routes, {
|
80 |
-
initialNavigation: '
|
81 |
scrollPositionRestoration: 'enabled',
|
82 |
-
anchorScrolling: 'enabled'
|
83 |
-
relativeLinkResolution: 'legacy'
|
84 |
})
|
85 |
],
|
86 |
exports: [RouterModule]
|
77 |
@NgModule({
|
78 |
imports: [
|
79 |
RouterModule.forRoot(routes, {
|
80 |
+
initialNavigation: 'enabledBlocking',
|
81 |
scrollPositionRestoration: 'enabled',
|
82 |
+
anchorScrolling: 'enabled'
|
|
|
83 |
})
|
84 |
],
|
85 |
exports: [RouterModule]
|
@@ -116,7 +116,7 @@
|
|
116 |
</ion-list>
|
117 |
</ion-content>
|
118 |
</ion-menu>
|
119 |
-
<!-- Main app content
|
120 |
<ion-router-outlet id="menu-content"></ion-router-outlet>
|
121 |
</ion-split-pane>
|
122 |
</ion-app>
|
116 |
</ion-list>
|
117 |
</ion-content>
|
118 |
</ion-menu>
|
119 |
+
<!-- Main app content gets rendered in this router-outlet -->
|
120 |
<ion-router-outlet id="menu-content"></ion-router-outlet>
|
121 |
</ion-split-pane>
|
122 |
</ion-app>
|
@@ -1,7 +1,8 @@
|
|
1 |
import { Component } from '@angular/core';
|
2 |
import { Router } from '@angular/router';
|
|
|
3 |
import { SplashScreen } from '@capacitor/splash-screen';
|
4 |
-
import {
|
5 |
|
6 |
@Component({
|
7 |
selector: 'app-root',
|
@@ -73,7 +74,7 @@ export class AppComponent {
|
|
73 |
|
74 |
public openTutorial(): void {
|
75 |
// save key to mark the walkthrough as NOT visited because the user wants to check it out
|
76 |
-
|
77 |
key: 'visitedWalkthrough',
|
78 |
value: 'false'
|
79 |
});
|
1 |
import { Component } from '@angular/core';
|
2 |
import { Router } from '@angular/router';
|
3 |
+
|
4 |
import { SplashScreen } from '@capacitor/splash-screen';
|
5 |
+
import { Preferences } from '@capacitor/preferences';
|
6 |
|
7 |
@Component({
|
8 |
selector: 'app-root',
|
74 |
|
75 |
public openTutorial(): void {
|
76 |
// save key to mark the walkthrough as NOT visited because the user wants to check it out
|
77 |
+
Preferences.set({
|
78 |
key: 'visitedWalkthrough',
|
79 |
value: 'false'
|
80 |
});
|
@@ -2,7 +2,6 @@ import { IonicModule } from '@ionic/angular';
|
|
2 |
import { RouterModule, Routes } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
5 |
-
import { FormsModule } from '@angular/forms';
|
6 |
|
7 |
import { ComponentsModule } from '../components/components.module';
|
8 |
|
@@ -19,7 +18,6 @@ const categoriesRoutes: Routes = [
|
|
19 |
imports: [
|
20 |
IonicModule,
|
21 |
CommonModule,
|
22 |
-
FormsModule,
|
23 |
RouterModule.forChild(categoriesRoutes),
|
24 |
ComponentsModule
|
25 |
],
|
2 |
import { RouterModule, Routes } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
|
|
5 |
|
6 |
import { ComponentsModule } from '../components/components.module';
|
7 |
|
18 |
imports: [
|
19 |
IonicModule,
|
20 |
CommonModule,
|
|
|
21 |
RouterModule.forChild(categoriesRoutes),
|
22 |
ComponentsModule
|
23 |
],
|
@@ -87,7 +87,7 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
|
|
87 |
}
|
88 |
|
89 |
constructor(
|
90 |
-
@Inject(PLATFORM_ID) private platformId: object
|
91 |
) { }
|
92 |
|
93 |
ngOnInit(): void {
|
87 |
}
|
88 |
|
89 |
constructor(
|
90 |
+
@Inject(PLATFORM_ID) private platformId: object
|
91 |
) { }
|
92 |
|
93 |
ngOnInit(): void {
|
@@ -1,8 +1,8 @@
|
|
1 |
import { Component, forwardRef, Input, OnChanges, ViewEncapsulation } from '@angular/core';
|
2 |
-
import {
|
3 |
|
4 |
export function counterRangeValidator(minValue, maxValue) {
|
5 |
-
return (c:
|
6 |
const err = {
|
7 |
rangeError: {
|
8 |
given: c.value,
|
@@ -71,7 +71,7 @@ export class CounterInputComponent implements ControlValueAccessor, OnChanges {
|
|
71 |
this.counterValue--;
|
72 |
}
|
73 |
|
74 |
-
validate(c:
|
75 |
return this.validateFn(c);
|
76 |
}
|
77 |
}
|
1 |
import { Component, forwardRef, Input, OnChanges, ViewEncapsulation } from '@angular/core';
|
2 |
+
import { UntypedFormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
|
3 |
|
4 |
export function counterRangeValidator(minValue, maxValue) {
|
5 |
+
return (c: UntypedFormControl) => {
|
6 |
const err = {
|
7 |
rangeError: {
|
8 |
given: c.value,
|
71 |
this.counterValue--;
|
72 |
}
|
73 |
|
74 |
+
validate(c: UntypedFormControl) {
|
75 |
return this.validateFn(c);
|
76 |
}
|
77 |
}
|
@@ -2,7 +2,6 @@ import { IonicModule } from '@ionic/angular';
|
|
2 |
import { RouterModule } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
5 |
-
import { FormsModule } from '@angular/forms';
|
6 |
|
7 |
import { ContactCardPage } from './contact-card.page';
|
8 |
import { ComponentsModule } from '../components/components.module';
|
@@ -11,7 +10,6 @@ import { ComponentsModule } from '../components/components.module';
|
|
11 |
imports: [
|
12 |
IonicModule,
|
13 |
CommonModule,
|
14 |
-
FormsModule,
|
15 |
ComponentsModule,
|
16 |
RouterModule.forChild([{ path: '', component: ContactCardPage }])
|
17 |
],
|
2 |
import { RouterModule } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
|
|
5 |
|
6 |
import { ContactCardPage } from './contact-card.page';
|
7 |
import { ComponentsModule } from '../components/components.module';
|
10 |
imports: [
|
11 |
IonicModule,
|
12 |
CommonModule,
|
|
|
13 |
ComponentsModule,
|
14 |
RouterModule.forChild([{ path: '', component: ContactCardPage }])
|
15 |
],
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -27,7 +26,6 @@ const routes: Routes = [
|
|
27 |
@NgModule({
|
28 |
imports: [
|
29 |
CommonModule,
|
30 |
-
FormsModule,
|
31 |
IonicModule,
|
32 |
RouterModule.forChild(routes),
|
33 |
ComponentsModule,
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
26 |
@NgModule({
|
27 |
imports: [
|
28 |
CommonModule,
|
|
|
29 |
IonicModule,
|
30 |
RouterModule.forChild(routes),
|
31 |
ComponentsModule,
|
@@ -1,5 +1,10 @@
|
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
import { ActivatedRoute } from '@angular/router';
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
@Component({
|
5 |
selector: 'app-deals-details',
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
import { ActivatedRoute } from '@angular/router';
|
3 |
+
import { IonicSwiper } from "@ionic/angular";
|
4 |
+
|
5 |
+
import SwiperCore, { Pagination } from "swiper";
|
6 |
+
|
7 |
+
SwiperCore.use([Pagination, IonicSwiper]);
|
8 |
|
9 |
@Component({
|
10 |
selector: 'app-deals-details',
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -25,7 +24,6 @@ const routes: Routes = [
|
|
25 |
@NgModule({
|
26 |
imports: [
|
27 |
CommonModule,
|
28 |
-
FormsModule,
|
29 |
IonicModule,
|
30 |
RouterModule.forChild(routes),
|
31 |
ComponentsModule,
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
|
|
27 |
IonicModule,
|
28 |
RouterModule.forChild(routes),
|
29 |
ComponentsModule,
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -26,7 +25,6 @@ const routes: Routes = [
|
|
26 |
@NgModule({
|
27 |
imports: [
|
28 |
CommonModule,
|
29 |
-
FormsModule,
|
30 |
IonicModule,
|
31 |
RouterModule.forChild(routes),
|
32 |
ComponentsModule,
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
25 |
@NgModule({
|
26 |
imports: [
|
27 |
CommonModule,
|
|
|
28 |
IonicModule,
|
29 |
RouterModule.forChild(routes),
|
30 |
ComponentsModule,
|
@@ -1,8 +1,14 @@
|
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
import { ActivatedRoute } from '@angular/router';
|
3 |
import { AlertController } from '@ionic/angular';
|
|
|
|
|
|
|
4 |
import { Subscription } from 'rxjs';
|
5 |
|
|
|
|
|
|
|
6 |
@Component({
|
7 |
selector: 'app-fashion-details',
|
8 |
templateUrl: './fashion-details.page.html',
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
import { ActivatedRoute } from '@angular/router';
|
3 |
import { AlertController } from '@ionic/angular';
|
4 |
+
import { IonicSwiper } from "@ionic/angular";
|
5 |
+
import SwiperCore, { Pagination } from "swiper";
|
6 |
+
|
7 |
import { Subscription } from 'rxjs';
|
8 |
|
9 |
+
SwiperCore.use([Pagination, IonicSwiper]);
|
10 |
+
|
11 |
+
|
12 |
@Component({
|
13 |
selector: 'app-fashion-details',
|
14 |
templateUrl: './fashion-details.page.html',
|
@@ -1,10 +1,8 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
7 |
-
|
8 |
import { ComponentsModule } from '../../components/components.module';
|
9 |
|
10 |
import { FashionService } from '../fashion.service';
|
@@ -24,7 +22,6 @@ const routes: Routes = [
|
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
27 |
-
FormsModule,
|
28 |
IonicModule,
|
29 |
RouterModule.forChild(routes),
|
30 |
ComponentsModule
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
|
|
6 |
import { ComponentsModule } from '../../components/components.module';
|
7 |
|
8 |
import { FashionService } from '../fashion.service';
|
22 |
@NgModule({
|
23 |
imports: [
|
24 |
CommonModule,
|
|
|
25 |
IonicModule,
|
26 |
RouterModule.forChild(routes),
|
27 |
ComponentsModule
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable } from '@angular/core';
|
2 |
+
|
3 |
+
import { FacebookAuthProvider, getAdditionalUserInfo, GoogleAuthProvider, OAuthProvider, TwitterAuthProvider } from '@angular/fire/auth';
|
4 |
+
import {
|
5 |
+
AuthCredential as FirebaseAuthCredential,
|
6 |
+
AuthProvider as FirebaseAuthProvider,
|
7 |
+
CustomParameters as FirebaseCustomParameters,
|
8 |
+
OAuthCredential,
|
9 |
+
User as FirebaseUser,
|
10 |
+
UserCredential as FirebaseUserCredential
|
11 |
+
} from '@angular/fire/auth';
|
12 |
+
|
13 |
+
import { AuthCredential, SignInResult, User, AdditionalUserInfo, SignInOptions } from '@capacitor-firebase/authentication';
|
14 |
+
|
15 |
+
|
16 |
+
// * Aux methods inspired on the @capacitor-firebase/authentication library
|
17 |
+
@Injectable()
|
18 |
+
export class FirebaseAuthHelper {
|
19 |
+
|
20 |
+
public applySignInOptions(options: SignInOptions, provider: OAuthProvider | GoogleAuthProvider | FacebookAuthProvider | TwitterAuthProvider) {
|
21 |
+
if (options.customParameters) {
|
22 |
+
const customParameters: FirebaseCustomParameters = {};
|
23 |
+
options.customParameters.map(parameter => {
|
24 |
+
customParameters[parameter.key] = parameter.value;
|
25 |
+
});
|
26 |
+
provider.setCustomParameters(customParameters);
|
27 |
+
}
|
28 |
+
if (options.scopes) {
|
29 |
+
for (const scope of options.scopes) {
|
30 |
+
provider.addScope(scope);
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
// ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L594)
|
36 |
+
public createSignInResult(userCredential: FirebaseUserCredential | null, authCredential: FirebaseAuthCredential | null): SignInResult {
|
37 |
+
const userResult = this.createUserResult(userCredential?.user || null);
|
38 |
+
const credentialResult = this.createCredentialResult(authCredential);
|
39 |
+
const additionalUserInfoResult = this.createAdditionalUserInfoResult(userCredential);
|
40 |
+
const result: SignInResult = {
|
41 |
+
user: userResult,
|
42 |
+
credential: credentialResult,
|
43 |
+
additionalUserInfo: additionalUserInfoResult
|
44 |
+
};
|
45 |
+
return result;
|
46 |
+
}
|
47 |
+
|
48 |
+
// ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L627)
|
49 |
+
public createUserResult(user: FirebaseUser | null): User | null {
|
50 |
+
if (!user) {
|
51 |
+
return null;
|
52 |
+
}
|
53 |
+
const result: User = {
|
54 |
+
displayName: user.displayName,
|
55 |
+
email: user.email,
|
56 |
+
emailVerified: user.emailVerified,
|
57 |
+
isAnonymous: user.isAnonymous,
|
58 |
+
phoneNumber: user.phoneNumber,
|
59 |
+
photoUrl: user.providerData[0].photoURL,
|
60 |
+
providerId: user.providerData[0].providerId,
|
61 |
+
tenantId: user.tenantId,
|
62 |
+
uid: user.uid
|
63 |
+
};
|
64 |
+
return result;
|
65 |
+
}
|
66 |
+
|
67 |
+
// ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L610)
|
68 |
+
private createCredentialResult(credential: FirebaseAuthCredential | null): AuthCredential | null {
|
69 |
+
if (!credential) {
|
70 |
+
return null;
|
71 |
+
}
|
72 |
+
const result: AuthCredential = {
|
73 |
+
providerId: credential.providerId
|
74 |
+
};
|
75 |
+
if (credential instanceof OAuthCredential) {
|
76 |
+
result.accessToken = credential.accessToken;
|
77 |
+
result.idToken = credential.idToken;
|
78 |
+
result.secret = credential.secret;
|
79 |
+
}
|
80 |
+
return result;
|
81 |
+
}
|
82 |
+
|
83 |
+
// ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L645)
|
84 |
+
private createAdditionalUserInfoResult(credential: FirebaseUserCredential | null): AdditionalUserInfo | null {
|
85 |
+
if (!credential) {
|
86 |
+
return null;
|
87 |
+
}
|
88 |
+
const additionalUserInfo = getAdditionalUserInfo(credential);
|
89 |
+
if (!additionalUserInfo) {
|
90 |
+
return null;
|
91 |
+
}
|
92 |
+
const { isNewUser, profile, providerId, username } = additionalUserInfo;
|
93 |
+
const result: AdditionalUserInfo = {
|
94 |
+
isNewUser
|
95 |
+
};
|
96 |
+
if (providerId !== null) {
|
97 |
+
result.providerId = providerId;
|
98 |
+
}
|
99 |
+
if (profile !== null) {
|
100 |
+
result.profile = profile as { [key: string]: unknown };
|
101 |
+
}
|
102 |
+
if (username !== null && username !== undefined) {
|
103 |
+
result.username = username;
|
104 |
+
}
|
105 |
+
return result;
|
106 |
+
}
|
107 |
+
}
|
@@ -11,6 +11,7 @@ import { provideAuth, getAuth, initializeAuth, indexedDBLocalPersistence } from
|
|
11 |
import { ComponentsModule } from '../../components/components.module';
|
12 |
import { environment } from '../../../environments/environment';
|
13 |
import { FirebaseAuthService } from './firebase-auth.service';
|
|
|
14 |
|
15 |
|
16 |
const routes: Routes = [
|
@@ -59,6 +60,9 @@ const routes: Routes = [
|
|
59 |
}
|
60 |
})
|
61 |
],
|
62 |
-
providers: [
|
|
|
|
|
|
|
63 |
})
|
64 |
export class FirebaseAuthModule {}
|
11 |
import { ComponentsModule } from '../../components/components.module';
|
12 |
import { environment } from '../../../environments/environment';
|
13 |
import { FirebaseAuthService } from './firebase-auth.service';
|
14 |
+
import { FirebaseAuthHelper } from './firebase-auth.helper';
|
15 |
|
16 |
|
17 |
const routes: Routes = [
|
60 |
}
|
61 |
})
|
62 |
],
|
63 |
+
providers: [
|
64 |
+
FirebaseAuthService,
|
65 |
+
FirebaseAuthHelper
|
66 |
+
]
|
67 |
})
|
68 |
export class FirebaseAuthModule {}
|
@@ -9,14 +9,14 @@ import { filter, map } from 'rxjs/operators';
|
|
9 |
import { AuthProvider, FacebookAuthProvider, GoogleAuthProvider, TwitterAuthProvider, OAuthProvider, OAuthCredential, UserCredential, createUserWithEmailAndPassword, getAuth, getRedirectResult, signInWithCredential, signInWithEmailAndPassword, signInWithPopup, signInWithRedirect, signOut } from '@angular/fire/auth';
|
10 |
|
11 |
import type {
|
12 |
-
|
13 |
-
User as FirebaseUser,
|
14 |
} from '@angular/fire/auth';
|
15 |
|
16 |
-
import {
|
17 |
|
18 |
import { FirebaseProfileModel } from './profile/firebase-profile.model';
|
19 |
import { SignInProvider } from './firebase-auth-definitions';
|
|
|
20 |
|
21 |
|
22 |
@Injectable({
|
@@ -33,6 +33,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
33 |
public route: ActivatedRoute,
|
34 |
public platform: Platform,
|
35 |
private ngZone: NgZone,
|
|
|
36 |
public loadingController: LoadingController,
|
37 |
public location: Location,
|
38 |
@Inject(PLATFORM_ID) private platformId: object
|
@@ -88,7 +89,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
88 |
break;
|
89 |
}
|
90 |
|
91 |
-
const signInResult =
|
92 |
|
93 |
this.dismissLoading();
|
94 |
|
@@ -130,6 +131,36 @@ export class FirebaseAuthService implements OnDestroy {
|
|
130 |
this.dismissLoading();
|
131 |
}
|
132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
public async signOut(): Promise<string> {
|
134 |
const signOutPromise = new Promise<string>((resolve, reject) => {
|
135 |
// * 1. Sign out on the native layer
|
@@ -154,15 +185,15 @@ export class FirebaseAuthService implements OnDestroy {
|
|
154 |
return signOutPromise;
|
155 |
}
|
156 |
|
157 |
-
private async socialSignIn(provider:
|
158 |
this.presentLoading(provider.providerId);
|
159 |
|
160 |
let authResult: SignInResult = null;
|
161 |
|
162 |
if (this.platform.is('capacitor')) {
|
163 |
-
authResult = await this.nativeAuth(provider,
|
164 |
} else {
|
165 |
-
authResult = await this.webAuth(provider);
|
166 |
}
|
167 |
|
168 |
this.dismissLoading();
|
@@ -174,66 +205,12 @@ export class FirebaseAuthService implements OnDestroy {
|
|
174 |
}
|
175 |
}
|
176 |
|
177 |
-
private
|
178 |
-
// ? Before invoking auth provider redirect flow, add a flag to the path.
|
179 |
-
// ? The presence of the flag in the path indicates we should wait for the auth redirect to complete
|
180 |
-
this.location.replaceState(this.location.path(), 'auth-redirect=' + authProviderId, this.location.getState());
|
181 |
-
}
|
182 |
-
|
183 |
-
private clearAuthWithProvidersRedirection(): void {
|
184 |
-
// ? Remove auth-redirect param from url
|
185 |
-
this.location.replaceState(this.router.url.split('?')[0], '');
|
186 |
-
this.dismissLoading();
|
187 |
-
}
|
188 |
-
|
189 |
-
private async presentLoading(authProviderId?: string): Promise<void> {
|
190 |
-
const authProviderCapitalized = authProviderId[0].toUpperCase() + authProviderId.slice(1);
|
191 |
-
|
192 |
-
this.loadingController.create({
|
193 |
-
message: authProviderId ? 'Signing in with ' + authProviderCapitalized : 'Signing in ...',
|
194 |
-
duration: 4000
|
195 |
-
}).then((loader) => {
|
196 |
-
this.authLoader = loader;
|
197 |
-
this.authLoader.present();
|
198 |
-
});
|
199 |
-
}
|
200 |
-
|
201 |
-
private async dismissLoading(): Promise<void> {
|
202 |
-
if (this.authLoader) {
|
203 |
-
await this.authLoader.dismiss();
|
204 |
-
}
|
205 |
-
}
|
206 |
-
|
207 |
-
private async webAuth(provider: AuthProvider, scopes?: Array<string>): Promise<SignInResult> {
|
208 |
-
// ? Scopes for Firebase JS SDK auth
|
209 |
-
if (scopes) {
|
210 |
-
let providerWithScopes: any;
|
211 |
-
|
212 |
-
switch (provider.providerId) {
|
213 |
-
case SignInProvider.apple:
|
214 |
-
providerWithScopes = (provider as OAuthProvider);
|
215 |
-
break;
|
216 |
-
case SignInProvider.facebook:
|
217 |
-
providerWithScopes = (provider as FacebookAuthProvider);
|
218 |
-
break;
|
219 |
-
case SignInProvider.google:
|
220 |
-
providerWithScopes = (provider as GoogleAuthProvider);
|
221 |
-
break;
|
222 |
-
case SignInProvider.twitter:
|
223 |
-
providerWithScopes = (provider as TwitterAuthProvider);
|
224 |
-
break;
|
225 |
-
}
|
226 |
-
|
227 |
-
scopes.forEach(scope => {
|
228 |
-
providerWithScopes.addScope(scope);
|
229 |
-
});
|
230 |
-
|
231 |
-
provider = providerWithScopes;
|
232 |
-
}
|
233 |
-
|
234 |
const auth = getAuth();
|
235 |
let webAuthUserCredential: UserCredential = null;
|
236 |
|
|
|
|
|
237 |
if (this.platform.is('desktop')) {
|
238 |
webAuthUserCredential = await signInWithPopup(auth, provider);
|
239 |
} else {
|
@@ -263,33 +240,28 @@ export class FirebaseAuthService implements OnDestroy {
|
|
263 |
break;
|
264 |
}
|
265 |
|
266 |
-
return this.createSignInResult(webAuthUserCredential
|
267 |
} else {
|
268 |
return Promise.reject('null webAuthUserCredential');
|
269 |
}
|
270 |
}
|
271 |
|
272 |
-
private async nativeAuth(provider: AuthProvider,
|
273 |
let nativeAuthResult: SignInResult = null;
|
274 |
|
275 |
-
// ? Scopes for Firebase native SDK (iOS and Android)
|
276 |
-
// TODO: Scopes for Firebase native SDK auth is a work in progress yet
|
277 |
-
// (see: https://github.com/robingenz/capacitor-firebase/issues/32)
|
278 |
-
|
279 |
-
|
280 |
// * 1. Sign in on the native layer
|
281 |
switch (provider.providerId) {
|
282 |
case SignInProvider.apple:
|
283 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithApple();
|
284 |
break;
|
285 |
case SignInProvider.facebook:
|
286 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithFacebook();
|
287 |
break;
|
288 |
case SignInProvider.google:
|
289 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithGoogle();
|
290 |
break;
|
291 |
case SignInProvider.twitter:
|
292 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithTwitter();
|
293 |
break;
|
294 |
}
|
295 |
|
@@ -312,11 +284,17 @@ export class FirebaseAuthService implements OnDestroy {
|
|
312 |
);
|
313 |
break;
|
314 |
case SignInProvider.google:
|
315 |
-
nativeCredential = GoogleAuthProvider.credential(
|
|
|
|
|
|
|
316 |
break;
|
317 |
case SignInProvider.twitter:
|
318 |
try {
|
319 |
-
nativeCredential = TwitterAuthProvider.credential(
|
|
|
|
|
|
|
320 |
break;
|
321 |
} catch (error) {
|
322 |
console.error(error);
|
@@ -326,7 +304,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
326 |
// * 2. Sign in on the web layer using the access token we got from the native sign in
|
327 |
const webAuthResult = await signInWithCredential(auth, nativeCredential);
|
328 |
|
329 |
-
return this.createSignInResult(webAuthResult
|
330 |
} else {
|
331 |
return Promise.reject('null nativeAuthResult');
|
332 |
}
|
@@ -334,34 +312,42 @@ export class FirebaseAuthService implements OnDestroy {
|
|
334 |
|
335 |
public async signInWithFacebook(): Promise<SignInResult> {
|
336 |
const provider = new FacebookAuthProvider();
|
337 |
-
const
|
|
|
|
|
338 |
|
339 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
340 |
-
return this.socialSignIn(provider,
|
341 |
}
|
342 |
|
343 |
public async signInWithGoogle(): Promise<SignInResult> {
|
344 |
const provider = new GoogleAuthProvider();
|
345 |
-
const
|
|
|
|
|
346 |
|
347 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
348 |
-
return this.socialSignIn(provider,
|
349 |
}
|
350 |
|
351 |
public async signInWithTwitter(): Promise<SignInResult> {
|
352 |
const provider = new TwitterAuthProvider();
|
353 |
-
const
|
|
|
|
|
354 |
|
355 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
356 |
-
return this.socialSignIn(provider,
|
357 |
}
|
358 |
|
359 |
public async signInWithApple(): Promise<SignInResult> {
|
360 |
const provider = new OAuthProvider('apple.com');
|
361 |
-
const
|
|
|
|
|
362 |
|
363 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
364 |
-
return this.socialSignIn(provider,
|
365 |
}
|
366 |
|
367 |
public async signInWithEmail(email: string, password: string): Promise<SignInResult> {
|
@@ -373,7 +359,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
373 |
|
374 |
this.dismissLoading();
|
375 |
|
376 |
-
return this.
|
377 |
}
|
378 |
|
379 |
public async signUpWithEmail(email: string, password: string): Promise<SignInResult> {
|
@@ -385,7 +371,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
385 |
|
386 |
this.dismissLoading();
|
387 |
|
388 |
-
return this.
|
389 |
}
|
390 |
|
391 |
public get redirectResult$(): Observable<any> {
|
@@ -402,7 +388,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
402 |
.pipe(
|
403 |
filter((user: FirebaseUser) => user != null),
|
404 |
map((user: FirebaseUser) => {
|
405 |
-
const userResult = this.createUserResult(user);
|
406 |
return this.setUserModelForProfile(userResult);
|
407 |
})
|
408 |
);
|
@@ -439,59 +425,4 @@ export class FirebaseAuthService implements OnDestroy {
|
|
439 |
return photoURL;
|
440 |
}
|
441 |
}
|
442 |
-
|
443 |
-
// * Aux methods inspired on the @capacitor-firebase/authentication library
|
444 |
-
|
445 |
-
// (see: https://github.com/robingenz/capacitor-firebase/blob/a51927ff3acce94cedcd7bfc218952bb106db904/packages/authentication/src/web.ts#L297)
|
446 |
-
private createSignInResultFromUserCredential(credential: UserCredential): SignInResult {
|
447 |
-
const userResult = this.createUserResult(credential.user);
|
448 |
-
const result: SignInResult = {
|
449 |
-
user: userResult,
|
450 |
-
credential: null,
|
451 |
-
};
|
452 |
-
return result;
|
453 |
-
}
|
454 |
-
|
455 |
-
private createSignInResult(user: FirebaseUser, credential: FirebaseAuthCredential | null): SignInResult {
|
456 |
-
const userResult = this.createUserResult(user);
|
457 |
-
const credentialResult = this.createCredentialResult(credential);
|
458 |
-
const result: SignInResult = {
|
459 |
-
user: userResult,
|
460 |
-
credential: credentialResult,
|
461 |
-
};
|
462 |
-
return result;
|
463 |
-
}
|
464 |
-
|
465 |
-
private createUserResult(user: FirebaseUser | null): User | null {
|
466 |
-
if (!user) {
|
467 |
-
return null;
|
468 |
-
}
|
469 |
-
const result: User = {
|
470 |
-
displayName: user.displayName,
|
471 |
-
email: user.email,
|
472 |
-
emailVerified: user.emailVerified,
|
473 |
-
isAnonymous: user.isAnonymous,
|
474 |
-
phoneNumber: user.phoneNumber,
|
475 |
-
photoUrl: user.photoURL,
|
476 |
-
providerId: user.providerId,
|
477 |
-
tenantId: user.tenantId,
|
478 |
-
uid: user.uid,
|
479 |
-
};
|
480 |
-
return result;
|
481 |
-
}
|
482 |
-
|
483 |
-
private createCredentialResult(credential: FirebaseAuthCredential | null): AuthCredential | null {
|
484 |
-
if (!credential) {
|
485 |
-
return null;
|
486 |
-
}
|
487 |
-
const result: AuthCredential = {
|
488 |
-
providerId: credential.providerId,
|
489 |
-
};
|
490 |
-
if (credential instanceof OAuthCredential) {
|
491 |
-
result.accessToken = credential.accessToken;
|
492 |
-
result.idToken = credential.idToken;
|
493 |
-
result.secret = credential.secret;
|
494 |
-
}
|
495 |
-
return result;
|
496 |
-
}
|
497 |
}
|
9 |
import { AuthProvider, FacebookAuthProvider, GoogleAuthProvider, TwitterAuthProvider, OAuthProvider, OAuthCredential, UserCredential, createUserWithEmailAndPassword, getAuth, getRedirectResult, signInWithCredential, signInWithEmailAndPassword, signInWithPopup, signInWithRedirect, signOut } from '@angular/fire/auth';
|
10 |
|
11 |
import type {
|
12 |
+
User as FirebaseUser
|
|
|
13 |
} from '@angular/fire/auth';
|
14 |
|
15 |
+
import { AuthStateChange, FirebaseAuthentication, SignInResult, SignInWithOAuthOptions, User } from '@capacitor-firebase/authentication';
|
16 |
|
17 |
import { FirebaseProfileModel } from './profile/firebase-profile.model';
|
18 |
import { SignInProvider } from './firebase-auth-definitions';
|
19 |
+
import { FirebaseAuthHelper } from './firebase-auth.helper';
|
20 |
|
21 |
|
22 |
@Injectable({
|
33 |
public route: ActivatedRoute,
|
34 |
public platform: Platform,
|
35 |
private ngZone: NgZone,
|
36 |
+
private firebaseAuthHelper: FirebaseAuthHelper,
|
37 |
public loadingController: LoadingController,
|
38 |
public location: Location,
|
39 |
@Inject(PLATFORM_ID) private platformId: object
|
89 |
break;
|
90 |
}
|
91 |
|
92 |
+
const signInResult = firebaseAuthHelper.createSignInResult(result, credential);
|
93 |
|
94 |
this.dismissLoading();
|
95 |
|
131 |
this.dismissLoading();
|
132 |
}
|
133 |
|
134 |
+
private prepareForAuthWithProvidersRedirection(authProviderId: string): void {
|
135 |
+
// ? Before invoking auth provider redirect flow, add a flag to the path.
|
136 |
+
// ? The presence of the flag in the path indicates we should wait for the auth redirect to complete
|
137 |
+
this.location.replaceState(this.location.path(), 'auth-redirect=' + authProviderId, this.location.getState());
|
138 |
+
}
|
139 |
+
|
140 |
+
private clearAuthWithProvidersRedirection(): void {
|
141 |
+
// ? Remove auth-redirect param from url
|
142 |
+
this.location.replaceState(this.router.url.split('?')[0], '');
|
143 |
+
this.dismissLoading();
|
144 |
+
}
|
145 |
+
|
146 |
+
private async presentLoading(authProviderId?: string): Promise<void> {
|
147 |
+
const authProviderCapitalized = authProviderId[0].toUpperCase() + authProviderId.slice(1);
|
148 |
+
|
149 |
+
this.loadingController.create({
|
150 |
+
message: authProviderId ? 'Signing in with ' + authProviderCapitalized : 'Signing in ...',
|
151 |
+
duration: 4000
|
152 |
+
}).then((loader) => {
|
153 |
+
this.authLoader = loader;
|
154 |
+
this.authLoader.present();
|
155 |
+
});
|
156 |
+
}
|
157 |
+
|
158 |
+
private async dismissLoading(): Promise<void> {
|
159 |
+
if (this.authLoader) {
|
160 |
+
await this.authLoader.dismiss();
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
public async signOut(): Promise<string> {
|
165 |
const signOutPromise = new Promise<string>((resolve, reject) => {
|
166 |
// * 1. Sign out on the native layer
|
185 |
return signOutPromise;
|
186 |
}
|
187 |
|
188 |
+
private async socialSignIn(provider: (OAuthProvider | GoogleAuthProvider | FacebookAuthProvider | TwitterAuthProvider), authOptions?: SignInWithOAuthOptions): Promise<SignInResult> {
|
189 |
this.presentLoading(provider.providerId);
|
190 |
|
191 |
let authResult: SignInResult = null;
|
192 |
|
193 |
if (this.platform.is('capacitor')) {
|
194 |
+
authResult = await this.nativeAuth(provider, authOptions);
|
195 |
} else {
|
196 |
+
authResult = await this.webAuth(provider, authOptions);
|
197 |
}
|
198 |
|
199 |
this.dismissLoading();
|
205 |
}
|
206 |
}
|
207 |
|
208 |
+
private async webAuth(provider: (OAuthProvider | GoogleAuthProvider | FacebookAuthProvider | TwitterAuthProvider), authOptions?: SignInWithOAuthOptions): Promise<SignInResult> {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
const auth = getAuth();
|
210 |
let webAuthUserCredential: UserCredential = null;
|
211 |
|
212 |
+
this.firebaseAuthHelper.applySignInOptions(authOptions || {}, provider);
|
213 |
+
|
214 |
if (this.platform.is('desktop')) {
|
215 |
webAuthUserCredential = await signInWithPopup(auth, provider);
|
216 |
} else {
|
240 |
break;
|
241 |
}
|
242 |
|
243 |
+
return this.firebaseAuthHelper.createSignInResult(webAuthUserCredential, webCredential);
|
244 |
} else {
|
245 |
return Promise.reject('null webAuthUserCredential');
|
246 |
}
|
247 |
}
|
248 |
|
249 |
+
private async nativeAuth(provider: AuthProvider, authOptions?: SignInWithOAuthOptions): Promise<SignInResult> {
|
250 |
let nativeAuthResult: SignInResult = null;
|
251 |
|
|
|
|
|
|
|
|
|
|
|
252 |
// * 1. Sign in on the native layer
|
253 |
switch (provider.providerId) {
|
254 |
case SignInProvider.apple:
|
255 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithApple(authOptions);
|
256 |
break;
|
257 |
case SignInProvider.facebook:
|
258 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithFacebook(authOptions);
|
259 |
break;
|
260 |
case SignInProvider.google:
|
261 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithGoogle(authOptions);
|
262 |
break;
|
263 |
case SignInProvider.twitter:
|
264 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithTwitter(authOptions);
|
265 |
break;
|
266 |
}
|
267 |
|
284 |
);
|
285 |
break;
|
286 |
case SignInProvider.google:
|
287 |
+
nativeCredential = GoogleAuthProvider.credential(
|
288 |
+
nativeAuthResult.credential?.idToken,
|
289 |
+
nativeAuthResult.credential?.accessToken
|
290 |
+
);
|
291 |
break;
|
292 |
case SignInProvider.twitter:
|
293 |
try {
|
294 |
+
nativeCredential = TwitterAuthProvider.credential(
|
295 |
+
nativeAuthResult.credential?.accessToken,
|
296 |
+
nativeAuthResult.credential?.secret
|
297 |
+
);
|
298 |
break;
|
299 |
} catch (error) {
|
300 |
console.error(error);
|
304 |
// * 2. Sign in on the web layer using the access token we got from the native sign in
|
305 |
const webAuthResult = await signInWithCredential(auth, nativeCredential);
|
306 |
|
307 |
+
return this.firebaseAuthHelper.createSignInResult(webAuthResult, nativeCredential);
|
308 |
} else {
|
309 |
return Promise.reject('null nativeAuthResult');
|
310 |
}
|
312 |
|
313 |
public async signInWithFacebook(): Promise<SignInResult> {
|
314 |
const provider = new FacebookAuthProvider();
|
315 |
+
const authOptions: SignInWithOAuthOptions = {
|
316 |
+
scopes: ['email', 'public_profile']
|
317 |
+
};
|
318 |
|
319 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
320 |
+
return this.socialSignIn(provider, authOptions);
|
321 |
}
|
322 |
|
323 |
public async signInWithGoogle(): Promise<SignInResult> {
|
324 |
const provider = new GoogleAuthProvider();
|
325 |
+
const authOptions: SignInWithOAuthOptions = {
|
326 |
+
scopes: ['email', 'profile']
|
327 |
+
};
|
328 |
|
329 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
330 |
+
return this.socialSignIn(provider, authOptions);
|
331 |
}
|
332 |
|
333 |
public async signInWithTwitter(): Promise<SignInResult> {
|
334 |
const provider = new TwitterAuthProvider();
|
335 |
+
const authOptions: SignInWithOAuthOptions = {
|
336 |
+
scopes: ['email', 'name']
|
337 |
+
};
|
338 |
|
339 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
340 |
+
return this.socialSignIn(provider, authOptions);
|
341 |
}
|
342 |
|
343 |
public async signInWithApple(): Promise<SignInResult> {
|
344 |
const provider = new OAuthProvider('apple.com');
|
345 |
+
const authOptions: SignInWithOAuthOptions = {
|
346 |
+
scopes: ['email', 'name']
|
347 |
+
};
|
348 |
|
349 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
350 |
+
return this.socialSignIn(provider, authOptions);
|
351 |
}
|
352 |
|
353 |
public async signInWithEmail(email: string, password: string): Promise<SignInResult> {
|
359 |
|
360 |
this.dismissLoading();
|
361 |
|
362 |
+
return this.firebaseAuthHelper.createSignInResult(credential, null);
|
363 |
}
|
364 |
|
365 |
public async signUpWithEmail(email: string, password: string): Promise<SignInResult> {
|
371 |
|
372 |
this.dismissLoading();
|
373 |
|
374 |
+
return this.firebaseAuthHelper.createSignInResult(credential, null);
|
375 |
}
|
376 |
|
377 |
public get redirectResult$(): Observable<any> {
|
388 |
.pipe(
|
389 |
filter((user: FirebaseUser) => user != null),
|
390 |
map((user: FirebaseUser) => {
|
391 |
+
const userResult = this.firebaseAuthHelper.createUserResult(user);
|
392 |
return this.setUserModelForProfile(userResult);
|
393 |
})
|
394 |
);
|
425 |
return photoURL;
|
426 |
}
|
427 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
}
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, NgZone } from '@angular/core';
|
2 |
-
import { Validators,
|
3 |
import { Router } from '@angular/router';
|
4 |
import { AuthStateChange, SignInResult } from '@capacitor-firebase/authentication';
|
5 |
|
@@ -15,7 +15,7 @@ import { FirebaseAuthService } from '../firebase-auth.service';
|
|
15 |
]
|
16 |
})
|
17 |
export class FirebaseSignInPage {
|
18 |
-
loginForm:
|
19 |
submitError: string;
|
20 |
authRedirectResult: Subscription;
|
21 |
|
@@ -35,12 +35,12 @@ export class FirebaseSignInPage {
|
|
35 |
public firebaseAuthService: FirebaseAuthService,
|
36 |
private ngZone: NgZone
|
37 |
) {
|
38 |
-
this.loginForm = new
|
39 |
-
'email': new
|
40 |
Validators.required,
|
41 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
42 |
])),
|
43 |
-
'password': new
|
44 |
Validators.minLength(6),
|
45 |
Validators.required
|
46 |
]))
|
1 |
import { Component, NgZone } from '@angular/core';
|
2 |
+
import { Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { Router } from '@angular/router';
|
4 |
import { AuthStateChange, SignInResult } from '@capacitor-firebase/authentication';
|
5 |
|
15 |
]
|
16 |
})
|
17 |
export class FirebaseSignInPage {
|
18 |
+
loginForm: UntypedFormGroup;
|
19 |
submitError: string;
|
20 |
authRedirectResult: Subscription;
|
21 |
|
35 |
public firebaseAuthService: FirebaseAuthService,
|
36 |
private ngZone: NgZone
|
37 |
) {
|
38 |
+
this.loginForm = new UntypedFormGroup({
|
39 |
+
'email': new UntypedFormControl('', Validators.compose([
|
40 |
Validators.required,
|
41 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
42 |
])),
|
43 |
+
'password': new UntypedFormControl('', Validators.compose([
|
44 |
Validators.minLength(6),
|
45 |
Validators.required
|
46 |
]))
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, OnInit, NgZone } from '@angular/core';
|
2 |
-
import { Validators,
|
3 |
import { Router } from '@angular/router';
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
|
@@ -18,8 +18,8 @@ import { FirebaseAuthService } from '../firebase-auth.service';
|
|
18 |
]
|
19 |
})
|
20 |
export class FirebaseSignUpPage implements OnInit {
|
21 |
-
signupForm:
|
22 |
-
matching_passwords_group:
|
23 |
submitError: string;
|
24 |
authRedirectResult: Subscription;
|
25 |
|
@@ -46,18 +46,18 @@ export class FirebaseSignUpPage implements OnInit {
|
|
46 |
public firebaseAuthService: FirebaseAuthService,
|
47 |
private ngZone: NgZone
|
48 |
) {
|
49 |
-
this.matching_passwords_group = new
|
50 |
-
'password': new
|
51 |
Validators.minLength(6),
|
52 |
Validators.required
|
53 |
])),
|
54 |
-
'confirm_password': new
|
55 |
-
}, (formGroup:
|
56 |
return PasswordValidator.areNotEqual(formGroup);
|
57 |
});
|
58 |
|
59 |
-
this.signupForm = new
|
60 |
-
'email': new
|
61 |
Validators.required,
|
62 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
63 |
])),
|
1 |
import { Component, OnInit, NgZone } from '@angular/core';
|
2 |
+
import { Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { Router } from '@angular/router';
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
|
18 |
]
|
19 |
})
|
20 |
export class FirebaseSignUpPage implements OnInit {
|
21 |
+
signupForm: UntypedFormGroup;
|
22 |
+
matching_passwords_group: UntypedFormGroup;
|
23 |
submitError: string;
|
24 |
authRedirectResult: Subscription;
|
25 |
|
46 |
public firebaseAuthService: FirebaseAuthService,
|
47 |
private ngZone: NgZone
|
48 |
) {
|
49 |
+
this.matching_passwords_group = new UntypedFormGroup({
|
50 |
+
'password': new UntypedFormControl('', Validators.compose([
|
51 |
Validators.minLength(6),
|
52 |
Validators.required
|
53 |
])),
|
54 |
+
'confirm_password': new UntypedFormControl('', Validators.required)
|
55 |
+
}, (formGroup: UntypedFormGroup) => {
|
56 |
return PasswordValidator.areNotEqual(formGroup);
|
57 |
});
|
58 |
|
59 |
+
this.signupForm = new UntypedFormGroup({
|
60 |
+
'email': new UntypedFormControl('', Validators.compose([
|
61 |
Validators.required,
|
62 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
63 |
])),
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
-
import {
|
3 |
import { ActivatedRoute } from '@angular/router';
|
4 |
|
5 |
import { Observable, ReplaySubject } from 'rxjs';
|
@@ -17,7 +17,7 @@ import { FirebaseCrudService } from '../firebase-crud.service';
|
|
17 |
],
|
18 |
})
|
19 |
export class FirebaseListingPage implements OnInit {
|
20 |
-
rangeForm:
|
21 |
searchQuery: string;
|
22 |
showAgeFilter = false;
|
23 |
|
@@ -34,8 +34,8 @@ export class FirebaseListingPage implements OnInit {
|
|
34 |
ngOnInit() {
|
35 |
this.searchQuery = '';
|
36 |
|
37 |
-
this.rangeForm = new
|
38 |
-
dual: new
|
39 |
});
|
40 |
|
41 |
this.route.data.subscribe((resolvedRouteData) => {
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
+
import { UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { ActivatedRoute } from '@angular/router';
|
4 |
|
5 |
import { Observable, ReplaySubject } from 'rxjs';
|
17 |
],
|
18 |
})
|
19 |
export class FirebaseListingPage implements OnInit {
|
20 |
+
rangeForm: UntypedFormGroup;
|
21 |
searchQuery: string;
|
22 |
showAgeFilter = false;
|
23 |
|
34 |
ngOnInit() {
|
35 |
this.searchQuery = '';
|
36 |
|
37 |
+
this.rangeForm = new UntypedFormGroup({
|
38 |
+
dual: new UntypedFormControl({lower: 1, upper: 100})
|
39 |
});
|
40 |
|
41 |
this.route.data.subscribe((resolvedRouteData) => {
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -25,7 +24,6 @@ const routes: Routes = [
|
|
25 |
@NgModule({
|
26 |
imports: [
|
27 |
CommonModule,
|
28 |
-
FormsModule,
|
29 |
IonicModule,
|
30 |
RouterModule.forChild(routes),
|
31 |
ComponentsModule,
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
|
|
27 |
IonicModule,
|
28 |
RouterModule.forChild(routes),
|
29 |
ComponentsModule,
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -25,7 +24,6 @@ const routes: Routes = [
|
|
25 |
@NgModule({
|
26 |
imports: [
|
27 |
CommonModule,
|
28 |
-
FormsModule,
|
29 |
IonicModule,
|
30 |
RouterModule.forChild(routes),
|
31 |
ComponentsModule,
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
|
|
27 |
IonicModule,
|
28 |
RouterModule.forChild(routes),
|
29 |
ComponentsModule,
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component } from '@angular/core';
|
2 |
-
import { Validators,
|
3 |
import { Router } from '@angular/router';
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
|
@@ -11,7 +11,7 @@ import { MenuController } from '@ionic/angular';
|
|
11 |
]
|
12 |
})
|
13 |
export class ForgotPasswordPage {
|
14 |
-
forgotPasswordForm:
|
15 |
|
16 |
validation_messages = {
|
17 |
'email': [
|
@@ -24,8 +24,8 @@ export class ForgotPasswordPage {
|
|
24 |
public router: Router,
|
25 |
public menu: MenuController
|
26 |
) {
|
27 |
-
this.forgotPasswordForm = new
|
28 |
-
'email': new
|
29 |
Validators.required,
|
30 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
31 |
]))
|
1 |
import { Component } from '@angular/core';
|
2 |
+
import { Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { Router } from '@angular/router';
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
|
11 |
]
|
12 |
})
|
13 |
export class ForgotPasswordPage {
|
14 |
+
forgotPasswordForm: UntypedFormGroup;
|
15 |
|
16 |
validation_messages = {
|
17 |
'email': [
|
24 |
public router: Router,
|
25 |
public menu: MenuController
|
26 |
) {
|
27 |
+
this.forgotPasswordForm = new UntypedFormGroup({
|
28 |
+
'email': new UntypedFormControl('test@test.com', Validators.compose([
|
29 |
Validators.required,
|
30 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
31 |
]))
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component} from '@angular/core';
|
2 |
-
import {
|
3 |
|
4 |
import { counterRangeValidator } from '../../components/counter-input/counter-input.component';
|
5 |
|
@@ -13,65 +13,65 @@ import { counterRangeValidator } from '../../components/counter-input/counter-in
|
|
13 |
})
|
14 |
export class FormsFiltersPage {
|
15 |
rangeForm: any;
|
16 |
-
checkboxForm:
|
17 |
-
radioForm:
|
18 |
-
checkboxTagsForm:
|
19 |
-
radioTagsForm:
|
20 |
-
switchersForm:
|
21 |
counterForm: any;
|
22 |
-
ratingForm:
|
23 |
-
radioColorForm:
|
24 |
|
25 |
constructor() {
|
26 |
-
this.rangeForm = new
|
27 |
-
single: new
|
28 |
-
dual: new
|
29 |
});
|
30 |
|
31 |
-
this.checkboxForm = new
|
32 |
-
person_1: new
|
33 |
-
person_2: new
|
34 |
-
person_3: new
|
35 |
-
person_4: new
|
36 |
-
person_5: new
|
37 |
});
|
38 |
|
39 |
-
this.radioForm = new
|
40 |
-
selected_option: new
|
41 |
});
|
42 |
|
43 |
-
this.checkboxTagsForm = new
|
44 |
-
tag_1: new
|
45 |
-
tag_2: new
|
46 |
-
tag_3: new
|
47 |
-
tag_4: new
|
48 |
-
tag_5: new
|
49 |
-
tag_6: new
|
50 |
-
tag_7: new
|
51 |
-
tag_8: new
|
52 |
});
|
53 |
|
54 |
-
this.radioTagsForm = new
|
55 |
-
selected_option: new
|
56 |
});
|
57 |
|
58 |
-
this.switchersForm = new
|
59 |
-
notifications: new
|
60 |
-
email_notifications: new
|
61 |
});
|
62 |
|
63 |
-
this.counterForm = new
|
64 |
-
counter: new
|
65 |
-
counter2: new
|
66 |
});
|
67 |
|
68 |
-
this.ratingForm = new
|
69 |
-
rate: new
|
70 |
-
rate2: new
|
71 |
});
|
72 |
|
73 |
-
this.radioColorForm = new
|
74 |
-
selected_color: new
|
75 |
});
|
76 |
}
|
77 |
|
1 |
import { Component} from '@angular/core';
|
2 |
+
import { UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
|
4 |
import { counterRangeValidator } from '../../components/counter-input/counter-input.component';
|
5 |
|
13 |
})
|
14 |
export class FormsFiltersPage {
|
15 |
rangeForm: any;
|
16 |
+
checkboxForm: UntypedFormGroup;
|
17 |
+
radioForm: UntypedFormGroup;
|
18 |
+
checkboxTagsForm: UntypedFormGroup;
|
19 |
+
radioTagsForm: UntypedFormGroup;
|
20 |
+
switchersForm: UntypedFormGroup;
|
21 |
counterForm: any;
|
22 |
+
ratingForm: UntypedFormGroup;
|
23 |
+
radioColorForm: UntypedFormGroup;
|
24 |
|
25 |
constructor() {
|
26 |
+
this.rangeForm = new UntypedFormGroup({
|
27 |
+
single: new UntypedFormControl(25),
|
28 |
+
dual: new UntypedFormControl({lower: 12, upper: 23})
|
29 |
});
|
30 |
|
31 |
+
this.checkboxForm = new UntypedFormGroup({
|
32 |
+
person_1: new UntypedFormControl(true),
|
33 |
+
person_2: new UntypedFormControl(false),
|
34 |
+
person_3: new UntypedFormControl(false),
|
35 |
+
person_4: new UntypedFormControl(true),
|
36 |
+
person_5: new UntypedFormControl(false)
|
37 |
});
|
38 |
|
39 |
+
this.radioForm = new UntypedFormGroup({
|
40 |
+
selected_option: new UntypedFormControl('apple')
|
41 |
});
|
42 |
|
43 |
+
this.checkboxTagsForm = new UntypedFormGroup({
|
44 |
+
tag_1: new UntypedFormControl(true),
|
45 |
+
tag_2: new UntypedFormControl(false),
|
46 |
+
tag_3: new UntypedFormControl(true),
|
47 |
+
tag_4: new UntypedFormControl(true),
|
48 |
+
tag_5: new UntypedFormControl(false),
|
49 |
+
tag_6: new UntypedFormControl(false),
|
50 |
+
tag_7: new UntypedFormControl({value: true, disabled: true}),
|
51 |
+
tag_8: new UntypedFormControl(false)
|
52 |
});
|
53 |
|
54 |
+
this.radioTagsForm = new UntypedFormGroup({
|
55 |
+
selected_option: new UntypedFormControl('any')
|
56 |
});
|
57 |
|
58 |
+
this.switchersForm = new UntypedFormGroup({
|
59 |
+
notifications: new UntypedFormControl(true),
|
60 |
+
email_notifications: new UntypedFormControl(false)
|
61 |
});
|
62 |
|
63 |
+
this.counterForm = new UntypedFormGroup({
|
64 |
+
counter: new UntypedFormControl(5, counterRangeValidator(1, 7)),
|
65 |
+
counter2: new UntypedFormControl(2, counterRangeValidator(1, 5))
|
66 |
});
|
67 |
|
68 |
+
this.ratingForm = new UntypedFormGroup({
|
69 |
+
rate: new UntypedFormControl(2.5),
|
70 |
+
rate2: new UntypedFormControl(1.5)
|
71 |
});
|
72 |
|
73 |
+
this.radioColorForm = new UntypedFormGroup({
|
74 |
+
selected_color: new UntypedFormControl('#fc9961')
|
75 |
});
|
76 |
}
|
77 |
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
-
import { Validators,
|
3 |
|
4 |
import { UsernameValidator } from '../../validators/username.validator';
|
5 |
import { PasswordValidator } from '../../validators/password.validator';
|
@@ -17,9 +17,9 @@ import { CountryPhone } from './country-phone.model';
|
|
17 |
})
|
18 |
export class FormsValidationsPage implements OnInit {
|
19 |
|
20 |
-
validationsForm:
|
21 |
-
matching_passwords_group:
|
22 |
-
country_phone_group:
|
23 |
countries: Array<CountryPhone>;
|
24 |
genders: Array<string>;
|
25 |
|
@@ -86,48 +86,48 @@ export class FormsValidationsPage implements OnInit {
|
|
86 |
'Other'
|
87 |
];
|
88 |
|
89 |
-
this.matching_passwords_group = new
|
90 |
-
password: new
|
91 |
Validators.minLength(5),
|
92 |
Validators.required,
|
93 |
Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')
|
94 |
])),
|
95 |
-
confirm_password: new
|
96 |
-
}, (formGroup:
|
97 |
return PasswordValidator.areNotEqual(formGroup);
|
98 |
});
|
99 |
|
100 |
-
const country = new
|
101 |
|
102 |
-
const phone = new
|
103 |
Validators.required,
|
104 |
PhoneValidator.invalidCountryPhone(country)
|
105 |
]));
|
106 |
-
this.country_phone_group = new
|
107 |
country: country,
|
108 |
phone: phone
|
109 |
});
|
110 |
|
111 |
-
this.validationsForm = new
|
112 |
-
'username': new
|
113 |
UsernameValidator.usernameNotAvailable,
|
114 |
Validators.maxLength(25),
|
115 |
Validators.minLength(5),
|
116 |
Validators.pattern('^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$'),
|
117 |
Validators.required
|
118 |
])),
|
119 |
-
'name': new
|
120 |
-
'lastname': new
|
121 |
-
'email': new
|
122 |
Validators.required,
|
123 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
124 |
])),
|
125 |
-
'gender': new
|
126 |
'country_phone': this.country_phone_group,
|
127 |
'matching_passwords': this.matching_passwords_group,
|
128 |
-
'guests': new
|
129 |
-
'bedrooms': new
|
130 |
-
'terms': new
|
131 |
});
|
132 |
}
|
133 |
|
1 |
import { Component, OnInit } from '@angular/core';
|
2 |
+
import { Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
|
4 |
import { UsernameValidator } from '../../validators/username.validator';
|
5 |
import { PasswordValidator } from '../../validators/password.validator';
|
17 |
})
|
18 |
export class FormsValidationsPage implements OnInit {
|
19 |
|
20 |
+
validationsForm: UntypedFormGroup;
|
21 |
+
matching_passwords_group: UntypedFormGroup;
|
22 |
+
country_phone_group: UntypedFormGroup;
|
23 |
countries: Array<CountryPhone>;
|
24 |
genders: Array<string>;
|
25 |
|
86 |
'Other'
|
87 |
];
|
88 |
|
89 |
+
this.matching_passwords_group = new UntypedFormGroup({
|
90 |
+
password: new UntypedFormControl('', Validators.compose([
|
91 |
Validators.minLength(5),
|
92 |
Validators.required,
|
93 |
Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')
|
94 |
])),
|
95 |
+
confirm_password: new UntypedFormControl('', Validators.required)
|
96 |
+
}, (formGroup: UntypedFormGroup) => {
|
97 |
return PasswordValidator.areNotEqual(formGroup);
|
98 |
});
|
99 |
|
100 |
+
const country = new UntypedFormControl(this.countries[0], Validators.required);
|
101 |
|
102 |
+
const phone = new UntypedFormControl('', Validators.compose([
|
103 |
Validators.required,
|
104 |
PhoneValidator.invalidCountryPhone(country)
|
105 |
]));
|
106 |
+
this.country_phone_group = new UntypedFormGroup({
|
107 |
country: country,
|
108 |
phone: phone
|
109 |
});
|
110 |
|
111 |
+
this.validationsForm = new UntypedFormGroup({
|
112 |
+
'username': new UntypedFormControl('', Validators.compose([
|
113 |
UsernameValidator.usernameNotAvailable,
|
114 |
Validators.maxLength(25),
|
115 |
Validators.minLength(5),
|
116 |
Validators.pattern('^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$'),
|
117 |
Validators.required
|
118 |
])),
|
119 |
+
'name': new UntypedFormControl('', Validators.required),
|
120 |
+
'lastname': new UntypedFormControl('', Validators.required),
|
121 |
+
'email': new UntypedFormControl('', Validators.compose([
|
122 |
Validators.required,
|
123 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
124 |
])),
|
125 |
+
'gender': new UntypedFormControl(this.genders[0], Validators.required),
|
126 |
'country_phone': this.country_phone_group,
|
127 |
'matching_passwords': this.matching_passwords_group,
|
128 |
+
'guests': new UntypedFormControl(6, counterRangeValidator(1, 12)),
|
129 |
+
'bedrooms': new UntypedFormControl(3, counterRangeValidator(1, 5)),
|
130 |
+
'terms': new UntypedFormControl(true, Validators.pattern('true'))
|
131 |
});
|
132 |
}
|
133 |
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, HostBinding, NgZone } from '@angular/core';
|
2 |
-
import {
|
3 |
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
import { IonicSwiper } from "@ionic/angular";
|
@@ -21,21 +21,21 @@ export class GettingStartedPage {
|
|
21 |
@HostBinding('class.last-slide-active') isLastSlide = false;
|
22 |
|
23 |
swiperRef: SwiperCore;
|
24 |
-
gettingStartedForm:
|
25 |
|
26 |
constructor(
|
27 |
public menu: MenuController,
|
28 |
private ngZone: NgZone
|
29 |
) {
|
30 |
-
this.gettingStartedForm = new
|
31 |
-
browsingCategory: new
|
32 |
-
followingInterests: new
|
33 |
-
tops: new
|
34 |
-
dresses: new
|
35 |
-
jeans: new
|
36 |
-
jackets: new
|
37 |
-
shoes: new
|
38 |
-
glasses: new
|
39 |
})
|
40 |
});
|
41 |
}
|
1 |
import { Component, HostBinding, NgZone } from '@angular/core';
|
2 |
+
import { UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
import { IonicSwiper } from "@ionic/angular";
|
21 |
@HostBinding('class.last-slide-active') isLastSlide = false;
|
22 |
|
23 |
swiperRef: SwiperCore;
|
24 |
+
gettingStartedForm: UntypedFormGroup;
|
25 |
|
26 |
constructor(
|
27 |
public menu: MenuController,
|
28 |
private ngZone: NgZone
|
29 |
) {
|
30 |
+
this.gettingStartedForm = new UntypedFormGroup({
|
31 |
+
browsingCategory: new UntypedFormControl('men'),
|
32 |
+
followingInterests: new UntypedFormGroup({
|
33 |
+
tops: new UntypedFormControl(true),
|
34 |
+
dresses: new UntypedFormControl(),
|
35 |
+
jeans: new UntypedFormControl(),
|
36 |
+
jackets: new UntypedFormControl(true),
|
37 |
+
shoes: new UntypedFormControl(),
|
38 |
+
glasses: new UntypedFormControl()
|
39 |
})
|
40 |
});
|
41 |
}
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component } from '@angular/core';
|
2 |
-
import { Validators,
|
3 |
import { Router } from '@angular/router';
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
|
@@ -11,7 +11,7 @@ import { MenuController } from '@ionic/angular';
|
|
11 |
]
|
12 |
})
|
13 |
export class LoginPage {
|
14 |
-
loginForm:
|
15 |
|
16 |
validation_messages = {
|
17 |
'email': [
|
@@ -28,12 +28,12 @@ export class LoginPage {
|
|
28 |
public router: Router,
|
29 |
public menu: MenuController
|
30 |
) {
|
31 |
-
this.loginForm = new
|
32 |
-
'email': new
|
33 |
Validators.required,
|
34 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
35 |
])),
|
36 |
-
'password': new
|
37 |
Validators.minLength(5),
|
38 |
Validators.required
|
39 |
]))
|
1 |
import { Component } from '@angular/core';
|
2 |
+
import { Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { Router } from '@angular/router';
|
4 |
import { MenuController } from '@ionic/angular';
|
5 |
|
11 |
]
|
12 |
})
|
13 |
export class LoginPage {
|
14 |
+
loginForm: UntypedFormGroup;
|
15 |
|
16 |
validation_messages = {
|
17 |
'email': [
|
28 |
public router: Router,
|
29 |
public menu: MenuController
|
30 |
) {
|
31 |
+
this.loginForm = new UntypedFormGroup({
|
32 |
+
'email': new UntypedFormControl('test@test.com', Validators.compose([
|
33 |
Validators.required,
|
34 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
35 |
])),
|
36 |
+
'password': new UntypedFormControl('', Validators.compose([
|
37 |
Validators.minLength(5),
|
38 |
Validators.required
|
39 |
]))
|
@@ -2,7 +2,6 @@ import { IonicModule } from '@ionic/angular';
|
|
2 |
import { RouterModule } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
5 |
-
import { FormsModule } from '@angular/forms';
|
6 |
|
7 |
import { ComponentsModule } from '../components/components.module';
|
8 |
|
@@ -14,7 +13,6 @@ import { NotificationsService } from '../notifications/notifications.service';
|
|
14 |
imports: [
|
15 |
IonicModule,
|
16 |
CommonModule,
|
17 |
-
FormsModule,
|
18 |
ComponentsModule,
|
19 |
RouterModule.forChild([
|
20 |
{
|
2 |
import { RouterModule } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
|
|
5 |
|
6 |
import { ComponentsModule } from '../components/components.module';
|
7 |
|
13 |
imports: [
|
14 |
IonicModule,
|
15 |
CommonModule,
|
|
|
16 |
ComponentsModule,
|
17 |
RouterModule.forChild([
|
18 |
{
|
@@ -2,7 +2,6 @@ import { IonicModule } from '@ionic/angular';
|
|
2 |
import { RouterModule } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
5 |
-
import { FormsModule } from '@angular/forms';
|
6 |
|
7 |
import { ComponentsModule } from '../components/components.module';
|
8 |
import { PageNotFoundPage } from './page-not-found.page';
|
@@ -12,7 +11,6 @@ import { PageNotFoundPage } from './page-not-found.page';
|
|
12 |
IonicModule,
|
13 |
CommonModule,
|
14 |
ComponentsModule,
|
15 |
-
FormsModule,
|
16 |
RouterModule.forChild([
|
17 |
{
|
18 |
path: '',
|
2 |
import { RouterModule } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
|
|
5 |
|
6 |
import { ComponentsModule } from '../components/components.module';
|
7 |
import { PageNotFoundPage } from './page-not-found.page';
|
11 |
IonicModule,
|
12 |
CommonModule,
|
13 |
ComponentsModule,
|
|
|
14 |
RouterModule.forChild([
|
15 |
{
|
16 |
path: '',
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -24,7 +23,6 @@ const routes: Routes = [
|
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
27 |
-
FormsModule,
|
28 |
IonicModule,
|
29 |
RouterModule.forChild(routes),
|
30 |
ComponentsModule
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
23 |
@NgModule({
|
24 |
imports: [
|
25 |
CommonModule,
|
|
|
26 |
IonicModule,
|
27 |
RouterModule.forChild(routes),
|
28 |
ComponentsModule
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -24,7 +23,6 @@ const routes: Routes = [
|
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
27 |
-
FormsModule,
|
28 |
IonicModule,
|
29 |
RouterModule.forChild(routes),
|
30 |
ComponentsModule
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
23 |
@NgModule({
|
24 |
imports: [
|
25 |
CommonModule,
|
|
|
26 |
IonicModule,
|
27 |
RouterModule.forChild(routes),
|
28 |
ComponentsModule
|
@@ -24,6 +24,8 @@ export class ImageShellComponent {
|
|
24 |
@HostBinding('class.img-loaded') imageLoaded = false;
|
25 |
@HostBinding('class.img-error') imageError = false;
|
26 |
|
|
|
|
|
27 |
@HostBinding('style.backgroundImage') backgroundImage: string;
|
28 |
|
29 |
@HostBinding('attr.display')
|
@@ -69,11 +71,12 @@ export class ImageShellComponent {
|
|
69 |
@Inject(PLATFORM_ID) private platformId: string
|
70 |
) {}
|
71 |
|
72 |
-
_imageLoaded() {
|
73 |
this.imageLoaded = true;
|
74 |
|
75 |
// If it's a cover image then set the background-image property accordingly
|
76 |
if (this._display === 'cover') {
|
|
|
77 |
this.backgroundImage = 'url(' + this._src + ')';
|
78 |
}
|
79 |
}
|
24 |
@HostBinding('class.img-loaded') imageLoaded = false;
|
25 |
@HostBinding('class.img-error') imageError = false;
|
26 |
|
27 |
+
@HostBinding('attr.data-error') errorMessage = 'Could not load image';
|
28 |
+
|
29 |
@HostBinding('style.backgroundImage') backgroundImage: string;
|
30 |
|
31 |
@HostBinding('attr.display')
|
71 |
@Inject(PLATFORM_ID) private platformId: string
|
72 |
) {}
|
73 |
|
74 |
+
_imageLoaded(): void {
|
75 |
this.imageLoaded = true;
|
76 |
|
77 |
// If it's a cover image then set the background-image property accordingly
|
78 |
if (this._display === 'cover') {
|
79 |
+
// Now that the image is loaded, set the background image
|
80 |
this.backgroundImage = 'url(' + this._src + ')';
|
81 |
}
|
82 |
}
|
@@ -2,7 +2,6 @@ import { IonicModule } from '@ionic/angular';
|
|
2 |
import { RouterModule, Routes } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
5 |
-
import { FormsModule } from '@angular/forms';
|
6 |
import { ComponentsModule } from '../components/components.module';
|
7 |
|
8 |
const showcaseRoutes: Routes = [
|
2 |
import { RouterModule, Routes } from '@angular/router';
|
3 |
import { NgModule } from '@angular/core';
|
4 |
import { CommonModule } from '@angular/common';
|
|
|
5 |
import { ComponentsModule } from '../components/components.module';
|
6 |
|
7 |
const showcaseRoutes: Routes = [
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component } from '@angular/core';
|
2 |
-
import { Validators,
|
3 |
import { Router } from '@angular/router';
|
4 |
import { ModalController, MenuController, IonRouterOutlet } from '@ionic/angular';
|
5 |
|
@@ -15,8 +15,8 @@ import { PasswordValidator } from '../validators/password.validator';
|
|
15 |
]
|
16 |
})
|
17 |
export class SignupPage {
|
18 |
-
signupForm:
|
19 |
-
matching_passwords_group:
|
20 |
|
21 |
validation_messages = {
|
22 |
'email': [
|
@@ -41,18 +41,18 @@ export class SignupPage {
|
|
41 |
public menu: MenuController,
|
42 |
private routerOutlet: IonRouterOutlet
|
43 |
) {
|
44 |
-
this.matching_passwords_group = new
|
45 |
-
'password': new
|
46 |
Validators.minLength(5),
|
47 |
Validators.required
|
48 |
])),
|
49 |
-
'confirm_password': new
|
50 |
-
}, (formGroup:
|
51 |
return PasswordValidator.areNotEqual(formGroup);
|
52 |
});
|
53 |
|
54 |
-
this.signupForm = new
|
55 |
-
'email': new
|
56 |
Validators.required,
|
57 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
58 |
])),
|
1 |
import { Component } from '@angular/core';
|
2 |
+
import { Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { Router } from '@angular/router';
|
4 |
import { ModalController, MenuController, IonRouterOutlet } from '@ionic/angular';
|
5 |
|
15 |
]
|
16 |
})
|
17 |
export class SignupPage {
|
18 |
+
signupForm: UntypedFormGroup;
|
19 |
+
matching_passwords_group: UntypedFormGroup;
|
20 |
|
21 |
validation_messages = {
|
22 |
'email': [
|
41 |
public menu: MenuController,
|
42 |
private routerOutlet: IonRouterOutlet
|
43 |
) {
|
44 |
+
this.matching_passwords_group = new UntypedFormGroup({
|
45 |
+
'password': new UntypedFormControl('', Validators.compose([
|
46 |
Validators.minLength(5),
|
47 |
Validators.required
|
48 |
])),
|
49 |
+
'confirm_password': new UntypedFormControl('', Validators.required)
|
50 |
+
}, (formGroup: UntypedFormGroup) => {
|
51 |
return PasswordValidator.areNotEqual(formGroup);
|
52 |
});
|
53 |
|
54 |
+
this.signupForm = new UntypedFormGroup({
|
55 |
+
'email': new UntypedFormControl('test@test.com', Validators.compose([
|
56 |
Validators.required,
|
57 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
58 |
])),
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -24,7 +23,6 @@ const routes: Routes = [
|
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
27 |
-
FormsModule,
|
28 |
IonicModule,
|
29 |
RouterModule.forChild(routes),
|
30 |
ComponentsModule
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
23 |
@NgModule({
|
24 |
imports: [
|
25 |
CommonModule,
|
|
|
26 |
IonicModule,
|
27 |
RouterModule.forChild(routes),
|
28 |
ComponentsModule
|
@@ -1,6 +1,5 @@
|
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import { FormsModule } from '@angular/forms';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
@@ -24,7 +23,6 @@ const routes: Routes = [
|
|
24 |
@NgModule({
|
25 |
imports: [
|
26 |
CommonModule,
|
27 |
-
FormsModule,
|
28 |
IonicModule,
|
29 |
RouterModule.forChild(routes),
|
30 |
ComponentsModule
|
1 |
import { NgModule } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
|
|
3 |
import { Routes, RouterModule } from '@angular/router';
|
4 |
|
5 |
import { IonicModule } from '@ionic/angular';
|
23 |
@NgModule({
|
24 |
imports: [
|
25 |
CommonModule,
|
|
|
26 |
IonicModule,
|
27 |
RouterModule.forChild(routes),
|
28 |
ComponentsModule
|
@@ -5,11 +5,11 @@ import { Routes, RouterModule } from '@angular/router';
|
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
7 |
|
8 |
-
import { ComponentsModule } from '../../components/components.module';
|
9 |
-
|
10 |
import { UserService } from '../user.service';
|
11 |
import { UserProfilePage } from './user-profile.page';
|
12 |
import { UserProfileResolver } from './user-profile.resolver';
|
|
|
|
|
13 |
|
14 |
const routes: Routes = [
|
15 |
{
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
7 |
|
|
|
|
|
8 |
import { UserService } from '../user.service';
|
9 |
import { UserProfilePage } from './user-profile.page';
|
10 |
import { UserProfileResolver } from './user-profile.resolver';
|
11 |
+
import { ComponentsModule } from '../../components/components.module';
|
12 |
+
|
13 |
|
14 |
const routes: Routes = [
|
15 |
{
|
@@ -1,17 +1,17 @@
|
|
1 |
-
import {
|
2 |
|
3 |
export class PasswordValidator {
|
4 |
|
5 |
// If our validation fails, we return an object with a key for the error name and a value of true.
|
6 |
// Otherwise, if the validation passes, we simply return null because there is no error.
|
7 |
|
8 |
-
static areNotEqual(formGroup:
|
9 |
let firstControlValue: any;
|
10 |
let valid = true;
|
11 |
|
12 |
for (const key in formGroup.controls) {
|
13 |
if (formGroup.controls.hasOwnProperty(key)) {
|
14 |
-
const control:
|
15 |
|
16 |
if (firstControlValue === undefined) {
|
17 |
firstControlValue = control.value;
|
1 |
+
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
2 |
|
3 |
export class PasswordValidator {
|
4 |
|
5 |
// If our validation fails, we return an object with a key for the error name and a value of true.
|
6 |
// Otherwise, if the validation passes, we simply return null because there is no error.
|
7 |
|
8 |
+
static areNotEqual(formGroup: UntypedFormGroup) {
|
9 |
let firstControlValue: any;
|
10 |
let valid = true;
|
11 |
|
12 |
for (const key in formGroup.controls) {
|
13 |
if (formGroup.controls.hasOwnProperty(key)) {
|
14 |
+
const control: UntypedFormControl = <UntypedFormControl>formGroup.controls[key];
|
15 |
|
16 |
if (firstControlValue === undefined) {
|
17 |
firstControlValue = control.value;
|
@@ -1,8 +1,8 @@
|
|
1 |
-
import {
|
2 |
|
3 |
export class UsernameValidator {
|
4 |
|
5 |
-
static usernameNotAvailable(fc:
|
6 |
|
7 |
// this is a dummy validator to check if the username is valid or not.
|
8 |
// In a real app you should check against your DB if the username is already in use.
|
1 |
+
import { UntypedFormControl } from '@angular/forms';
|
2 |
|
3 |
export class UsernameValidator {
|
4 |
|
5 |
+
static usernameNotAvailable(fc: UntypedFormControl) {
|
6 |
|
7 |
// this is a dummy validator to check if the username is valid or not.
|
8 |
// In a real app you should check against your DB if the username is already in use.
|
@@ -1,13 +1,14 @@
|
|
1 |
import { Injectable } from '@angular/core';
|
2 |
import { CanActivate, Router } from '@angular/router';
|
3 |
-
|
|
|
4 |
|
5 |
@Injectable()
|
6 |
export class WalkthroughGuard implements CanActivate {
|
7 |
constructor(private router: Router) {}
|
8 |
|
9 |
async canActivate(): Promise<boolean> {
|
10 |
-
const { value } = await
|
11 |
|
12 |
if (value === 'true') {
|
13 |
// this is a returning user, don't show him the walkthrough
|
1 |
import { Injectable } from '@angular/core';
|
2 |
import { CanActivate, Router } from '@angular/router';
|
3 |
+
|
4 |
+
import { Preferences } from '@capacitor/preferences';
|
5 |
|
6 |
@Injectable()
|
7 |
export class WalkthroughGuard implements CanActivate {
|
8 |
constructor(private router: Router) {}
|
9 |
|
10 |
async canActivate(): Promise<boolean> {
|
11 |
+
const { value } = await Preferences.get({ key: 'visitedWalkthrough' });
|
12 |
|
13 |
if (value === 'true') {
|
14 |
// this is a returning user, don't show him the walkthrough
|
@@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms';
|
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
|
|
7 |
import { SwiperModule } from 'swiper/angular';
|
8 |
|
9 |
import { ComponentsModule } from '../components/components.module';
|
4 |
import { Routes, RouterModule } from '@angular/router';
|
5 |
|
6 |
import { IonicModule } from '@ionic/angular';
|
7 |
+
|
8 |
import { SwiperModule } from 'swiper/angular';
|
9 |
|
10 |
import { ComponentsModule } from '../components/components.module';
|
@@ -1,9 +1,8 @@
|
|
1 |
import { Component, AfterViewInit, ViewChild, HostBinding, OnInit, NgZone } from '@angular/core';
|
2 |
|
3 |
-
import {
|
4 |
|
5 |
-
import { MenuController } from '@ionic/angular';
|
6 |
-
import { IonicSwiper } from '@ionic/angular';
|
7 |
|
8 |
import SwiperCore, { Pagination } from 'swiper';
|
9 |
import { SwiperComponent } from 'swiper/angular';
|
@@ -34,8 +33,8 @@ export class WalkthroughPage implements AfterViewInit, OnInit {
|
|
34 |
) { }
|
35 |
|
36 |
ngOnInit(): void {
|
37 |
-
// save key to mark the walkthrough as visited so the next time the user
|
38 |
-
|
39 |
key: 'visitedWalkthrough',
|
40 |
value: 'true'
|
41 |
});
|
@@ -53,7 +52,6 @@ export class WalkthroughPage implements AfterViewInit, OnInit {
|
|
53 |
|
54 |
ngAfterViewInit(): void {
|
55 |
// Accessing slides in server platform throw errors
|
56 |
-
// if (isPlatformBrowser(this.platformId)) {
|
57 |
this.swiperRef = this.swiper.swiperRef;
|
58 |
|
59 |
this.swiperRef.on('slidesLengthChange', () => {
|
@@ -71,7 +69,6 @@ export class WalkthroughPage implements AfterViewInit, OnInit {
|
|
71 |
this.markSlides(this.swiperRef);
|
72 |
});
|
73 |
});
|
74 |
-
// }
|
75 |
}
|
76 |
|
77 |
public setSwiperInstance(swiper: SwiperCore): void {
|
1 |
import { Component, AfterViewInit, ViewChild, HostBinding, OnInit, NgZone } from '@angular/core';
|
2 |
|
3 |
+
import { Preferences } from '@capacitor/preferences';
|
4 |
|
5 |
+
import { MenuController, IonicSwiper } from '@ionic/angular';
|
|
|
6 |
|
7 |
import SwiperCore, { Pagination } from 'swiper';
|
8 |
import { SwiperComponent } from 'swiper/angular';
|
33 |
) { }
|
34 |
|
35 |
ngOnInit(): void {
|
36 |
+
// save key to mark the walkthrough as visited so the next time the user visits the app, he would be redirected to log in
|
37 |
+
Preferences.set({
|
38 |
key: 'visitedWalkthrough',
|
39 |
value: 'true'
|
40 |
});
|
52 |
|
53 |
ngAfterViewInit(): void {
|
54 |
// Accessing slides in server platform throw errors
|
|
|
55 |
this.swiperRef = this.swiper.swiperRef;
|
56 |
|
57 |
this.swiperRef.on('slidesLengthChange', () => {
|
69 |
this.markSlides(this.swiperRef);
|
70 |
});
|
71 |
});
|
|
|
72 |
}
|
73 |
|
74 |
public setSwiperInstance(swiper: SwiperCore): void {
|
@@ -1,5 +1,5 @@
|
|
1 |
// http://ionicframework.com/docs/theming/
|
2 |
-
@import url(
|
3 |
|
4 |
@import "~@ionic/angular/css/core.css";
|
5 |
@import "~@ionic/angular/css/normalize.css";
|
@@ -12,10 +12,10 @@
|
|
12 |
@import "~@ionic/angular/css/text-transformation.css";
|
13 |
@import "~@ionic/angular/css/flex-utils.css";
|
14 |
|
15 |
-
@import "
|
16 |
-
@import "
|
17 |
-
@import "
|
18 |
-
@import "
|
19 |
|
20 |
// EXTRA GLOBAL STYLES
|
21 |
// Add custom Ionic Colors
|
1 |
// http://ionicframework.com/docs/theming/
|
2 |
+
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,700");
|
3 |
|
4 |
@import "~@ionic/angular/css/core.css";
|
5 |
@import "~@ionic/angular/css/normalize.css";
|
12 |
@import "~@ionic/angular/css/text-transformation.css";
|
13 |
@import "~@ionic/angular/css/flex-utils.css";
|
14 |
|
15 |
+
@import "swiper/scss";
|
16 |
+
@import "swiper/scss/autoplay";
|
17 |
+
@import "swiper/scss/pagination";
|
18 |
+
@import "@ionic/angular/css/ionic-swiper";
|
19 |
|
20 |
// EXTRA GLOBAL STYLES
|
21 |
// Add custom Ionic Colors
|
@@ -17,8 +17,9 @@
|
|
17 |
<meta name="keywords" content="">
|
18 |
<!-- favicon -->
|
19 |
<!-- If you have a multi size .ico file -->
|
20 |
-
<link rel="shortcut icon" sizes="64x64 48x48 32x32 24x24 16x16" href="assets/icon/favicon.ico">
|
21 |
<!-- If you don't -->
|
|
|
22 |
<link rel="icon" type="image/png" sizes="64x64" href="assets/icon/favicon-64x64.png">
|
23 |
<link rel="icon" type="image/png" sizes="48x48" href="assets/icon/favicon-48x48.png">
|
24 |
<link rel="icon" type="image/png" sizes="32x32" href="assets/icon/favicon-32x32.png">
|
17 |
<meta name="keywords" content="">
|
18 |
<!-- favicon -->
|
19 |
<!-- If you have a multi size .ico file -->
|
20 |
+
<link rel="shortcut icon" sizes="128x128 64x64 48x48 32x32 24x24 16x16" href="assets/icon/favicon.ico">
|
21 |
<!-- If you don't -->
|
22 |
+
<link rel="icon" type="image/png" sizes="128x128" href="assets/icon/favicon-128x128.png">
|
23 |
<link rel="icon" type="image/png" sizes="64x64" href="assets/icon/favicon-64x64.png">
|
24 |
<link rel="icon" type="image/png" sizes="48x48" href="assets/icon/favicon-48x48.png">
|
25 |
<link rel="icon" type="image/png" sizes="32x32" href="assets/icon/favicon-32x32.png">
|
@@ -1,3 +1,7 @@
|
|
|
|
|
|
|
|
|
|
1 |
ion-title {
|
2 |
// Adding !important to force precedence in SSR
|
3 |
font-weight: 400 !important;
|
@@ -13,8 +17,3 @@ ion-title {
|
|
13 |
margin-inline-start: .5rem;
|
14 |
}
|
15 |
}
|
16 |
-
|
17 |
-
// temporary fix for https://github.com/ionic-team/ionic-framework/issues/18641
|
18 |
-
ion-picker {
|
19 |
-
touch-action: none;
|
20 |
-
}
|
1 |
+
html {
|
2 |
+
line-height: 1.5;
|
3 |
+
}
|
4 |
+
|
5 |
ion-title {
|
6 |
// Adding !important to force precedence in SSR
|
7 |
font-weight: 400 !important;
|
17 |
margin-inline-start: .5rem;
|
18 |
}
|
19 |
}
|
|
|
|
|
|
|
|
|
|
@@ -13,6 +13,5 @@
|
|
13 |
"src/**/*.d.ts"
|
14 |
],
|
15 |
"angularCompilerOptions": {
|
16 |
-
"enableIvy": true
|
17 |
}
|
18 |
}
|
13 |
"src/**/*.d.ts"
|
14 |
],
|
15 |
"angularCompilerOptions": {
|
|
|
16 |
}
|
17 |
}
|
@@ -12,7 +12,7 @@
|
|
12 |
"allowSyntheticDefaultImports": true,
|
13 |
"emitDecoratorMetadata": true,
|
14 |
"experimentalDecorators": true,
|
15 |
-
"target": "
|
16 |
"skipLibCheck": true,
|
17 |
"typeRoots": [
|
18 |
"node_modules/@types"
|
@@ -20,6 +20,7 @@
|
|
20 |
"lib": [
|
21 |
"es2018",
|
22 |
"dom"
|
23 |
-
]
|
|
|
24 |
}
|
25 |
}
|
12 |
"allowSyntheticDefaultImports": true,
|
13 |
"emitDecoratorMetadata": true,
|
14 |
"experimentalDecorators": true,
|
15 |
+
"target": "ES2022",
|
16 |
"skipLibCheck": true,
|
17 |
"typeRoots": [
|
18 |
"node_modules/@types"
|
20 |
"lib": [
|
21 |
"es2018",
|
22 |
"dom"
|
23 |
+
],
|
24 |
+
"useDefineForClassFields": false
|
25 |
}
|
26 |
}
|