@@ -1,37 +1,23 @@
|
|
1 |
{
|
2 |
"projects": {
|
3 |
"dev": "dev-ion4fullpwa",
|
4 |
-
"prod": "ion4fullpwa",
|
5 |
-
"pro": "pro-ion4fullpwa",
|
6 |
"elite": "elite-ionic-6-full-app"
|
7 |
},
|
8 |
"targets": {
|
9 |
-
"
|
10 |
-
"hosting": {
|
11 |
-
"ionic-5": [
|
12 |
-
"dev-ion4fullpwa"
|
13 |
-
],
|
14 |
-
"ionic-6": [
|
15 |
-
"dev-ionic-6-full-app"
|
16 |
-
]
|
17 |
-
}
|
18 |
-
},
|
19 |
-
"pro-ion4fullpwa": {
|
20 |
"hosting": {
|
21 |
-
"
|
22 |
-
"
|
23 |
-
],
|
24 |
-
"06-2022-release": [
|
25 |
-
"pro-ionic-6-full-app"
|
26 |
]
|
27 |
}
|
28 |
},
|
29 |
-
"
|
30 |
"hosting": {
|
31 |
-
"
|
32 |
-
"
|
33 |
]
|
34 |
}
|
35 |
}
|
36 |
-
}
|
|
|
37 |
}
|
1 |
{
|
2 |
"projects": {
|
3 |
"dev": "dev-ion4fullpwa",
|
|
|
|
|
4 |
"elite": "elite-ionic-6-full-app"
|
5 |
},
|
6 |
"targets": {
|
7 |
+
"elite-ionic-6-full-app": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
"hosting": {
|
9 |
+
"02-2023-release": [
|
10 |
+
"elite-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,32 +1,34 @@
|
|
1 |
## Generate diff file
|
2 |
*PRO vs ELITE (**no /android or /ios configs**)*
|
3 |
```bash
|
4 |
-
git diff pro-version..elite-
|
5 |
```
|
6 |
|
7 |
*PRO vs ELITE (**android config**)*
|
8 |
```bash
|
9 |
-
git diff pro-version..elite-
|
10 |
```
|
11 |
|
12 |
*PRO vs ELITE (**ios config**)*
|
13 |
```bash
|
14 |
-
git diff pro-version..elite-
|
15 |
```
|
16 |
|
17 |
-
|
|
|
|
|
18 |
```bash
|
19 |
-
git diff
|
20 |
```
|
21 |
|
22 |
-
*Last
|
23 |
```bash
|
24 |
-
git diff
|
25 |
```
|
26 |
|
27 |
-
*Last
|
28 |
```bash
|
29 |
-
git diff
|
30 |
```
|
31 |
|
32 |
## Generate visual diff HTML file
|
@@ -38,11 +40,11 @@ diff2html --style side --file diff/previews/pro-vs-elite_android-changelog.html
|
|
38 |
|
39 |
diff2html --style side --file diff/previews/pro-vs-elite_ios-changelog.html --input file -- diff/pro-vs-elite_ios-changelog.diff
|
40 |
|
41 |
-
diff2html --style side --file diff/previews/last-
|
42 |
|
43 |
-
diff2html --style side --file diff/previews/last-
|
44 |
|
45 |
-
diff2html --style side --file diff/previews/last-
|
46 |
```
|
47 |
|
48 |
*To open the visual diff on the browser*
|
@@ -53,9 +55,9 @@ diff2html --style side --input file -- diff/pro-vs-elite_android-changelog.diff
|
|
53 |
|
54 |
diff2html --style side --input file -- diff/pro-vs-elite_ios-changelog.diff
|
55 |
|
56 |
-
diff2html --style side --input file -- diff/last-
|
57 |
|
58 |
-
diff2html --style side --input file -- diff/last-
|
59 |
|
60 |
-
diff2html --style side --input file -- diff/last-
|
61 |
```
|
1 |
## Generate diff file
|
2 |
*PRO vs ELITE (**no /android or /ios configs**)*
|
3 |
```bash
|
4 |
+
git diff pro-version..elite-update-1 --diff-filter=ADCMRT ':!package-lock.json' ':!dist' ':!diff' ':!.gradle/*' ':!android' ':!ios' ':!*.diff' ':!*.png' ':!*.svg' > diff/pro-vs-elite_changelog.diff
|
5 |
```
|
6 |
|
7 |
*PRO vs ELITE (**android config**)*
|
8 |
```bash
|
9 |
+
git diff pro-version..elite-update-1 --diff-filter=ADCMRT -- android/ ':!*.svg' > diff/pro-vs-elite_android-changelog.diff
|
10 |
```
|
11 |
|
12 |
*PRO vs ELITE (**ios config**)*
|
13 |
```bash
|
14 |
+
git diff pro-version..elite-update-1 --diff-filter=ADCMRT -- ios/ > diff/pro-vs-elite_ios-changelog.diff
|
15 |
```
|
16 |
|
17 |
+
---
|
18 |
+
|
19 |
+
*Last ELITE update (**11-2022**) vs Current ELITE update (**02-2023**)*
|
20 |
```bash
|
21 |
+
git diff 11-2022_elite-release..elite-update-1 --diff-filter=ADCMRT ':!package-lock.json' ':!dist' ':!diff' ':!.gradle/*' ':!android' ':!ios' ':!*.diff' ':!*.png' ':!*.svg' > diff/last-elite-update-vs-current-elite_changelog.diff
|
22 |
```
|
23 |
|
24 |
+
*Last ELITE update (**11-2022**) vs Current ELITE update (**02-2023**) (**android config**)*
|
25 |
```bash
|
26 |
+
git diff 11-2022_elite-release..elite-update-1 --diff-filter=ADCMRT -- android/ ':!*.svg' > diff/last-elite-update-vs-current-elite_android-changelog.diff
|
27 |
```
|
28 |
|
29 |
+
*Last ELITE update (**11-2022**) vs Current ELITE update (**02-2023**) (**ios config**)*
|
30 |
```bash
|
31 |
+
git diff 11-2022_elite-release..elite-update-1 --diff-filter=ADCMRT -- ios/ > diff/last-elite-update-vs-current-elite_ios-changelog.diff
|
32 |
```
|
33 |
|
34 |
## Generate visual diff HTML file
|
40 |
|
41 |
diff2html --style side --file diff/previews/pro-vs-elite_ios-changelog.html --input file -- diff/pro-vs-elite_ios-changelog.diff
|
42 |
|
43 |
+
diff2html --style side --file diff/previews/last-elite-update-vs-current-elite_changelog.html --input file -- diff/last-elite-update-vs-current-elite_changelog.diff
|
44 |
|
45 |
+
diff2html --style side --file diff/previews/last-elite-update-vs-current-elite_android-changelog.html --input file -- diff/last-elite-update-vs-current-elite_android-changelog.diff
|
46 |
|
47 |
+
diff2html --style side --file diff/previews/last-elite-update-vs-current-elite_ios-changelog.html --input file -- diff/last-elite-update-vs-current-elite_ios-changelog.diff
|
48 |
```
|
49 |
|
50 |
*To open the visual diff on the browser*
|
55 |
|
56 |
diff2html --style side --input file -- diff/pro-vs-elite_ios-changelog.diff
|
57 |
|
58 |
+
diff2html --style side --input file -- diff/last-elite-update-vs-current-elite_changelog.diff
|
59 |
|
60 |
+
diff2html --style side --input file -- diff/last-elite-update-vs-current-elite_android-changelog.diff
|
61 |
|
62 |
+
diff2html --style side --input file -- diff/last-elite-update-vs-current-elite_ios-changelog.diff
|
63 |
```
|
@@ -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
|
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 = ionic-5
|
48 |
- RESOURCE_IDENTIFIER (the SITE_ID) = dev-ion4fullpwa
|
49 |
``` bash
|
@@ -63,11 +64,11 @@ firebase target:apply hosting 11-2022-release elite-ionic-6-full-app -P elite
|
|
63 |
```
|
64 |
|
65 |
|
66 |
-
|
67 |
Don't forget to configure this before deploying to the new target
|
68 |
|
69 |
|
70 |
-
|
71 |
``` bash
|
72 |
firebase deploy --only hosting:ionic-6 -P dev
|
73 |
```
|
@@ -76,9 +77,90 @@ firebase deploy --only hosting:ionic-6 -P dev
|
|
76 |
firebase deploy --only hosting:11-2022-release -P elite
|
77 |
```
|
78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
82 |
``` bash
|
83 |
-
firebase hosting:channel:deploy
|
84 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
- elite-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 |
+
elite (elite-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 = ionic-5
|
49 |
- RESOURCE_IDENTIFIER (the SITE_ID) = dev-ion4fullpwa
|
50 |
``` bash
|
64 |
```
|
65 |
|
66 |
|
67 |
+
### [Configure your `firebase.json` file to use deploy targets](https://firebase.google.com/docs/cli/targets#configure_your_firebasejson_file_to_use_deploy_targets)
|
68 |
Don't forget to configure this before deploying to the new target
|
69 |
|
70 |
|
71 |
+
### Deploy to specific target (i.e.: different site)
|
72 |
``` bash
|
73 |
firebase deploy --only hosting:ionic-6 -P dev
|
74 |
```
|
77 |
firebase deploy --only hosting:11-2022-release -P elite
|
78 |
```
|
79 |
|
80 |
+
``` bash
|
81 |
+
firebase deploy --only hosting:02-2023-release -P elite
|
82 |
+
```
|
83 |
+
|
84 |
+
|
85 |
+
### Create preview channel
|
86 |
+
> Preview channels don't have Firebase auth configured automatically `Unable to add channel domain to Firebase Auth`
|
87 |
+
- CHANNEL_ID = 0-2-0
|
88 |
+
- TARGET_NAME = 02-2023-release
|
89 |
+
- PROJECT_ALIAS = dev
|
90 |
+
``` bash
|
91 |
+
firebase hosting:channel:deploy 0-2-0 --only 02-2023-release -P dev
|
92 |
+
```
|
93 |
+
|
94 |
+
|
95 |
+
---
|
96 |
+
|
97 |
+
# Update release process
|
98 |
+
|
99 |
+
## DEV
|
100 |
+
First test the changes on the dev site (dev-ionic-6-full-app)
|
101 |
+
|
102 |
+
|
103 |
+
### 1 - Create new target
|
104 |
+
> This updates the `.firebaserc` file
|
105 |
+
|
106 |
+
- TARGET_NAME = 02-2023-release
|
107 |
+
- RESOURCE_IDENTIFIER (the SITE_ID) = dev-ionic-6-full-app
|
108 |
+
- PROJECT_ALIAS = dev
|
109 |
+
``` bash
|
110 |
+
firebase target:apply hosting 02-2023-release dev-ionic-6-full-app -P dev
|
111 |
+
```
|
112 |
+
|
113 |
+
|
114 |
+
### 2 - Update firebase.json
|
115 |
+
> Manually update the file and add the new target (`02-2023-release`) config (just copy and paste from the previous one)
|
116 |
+
|
117 |
|
118 |
+
### [OPTIONAL] - Create preview channel for the latest update you want to test live
|
119 |
+
> Preview channels don't have Firebase auth configured automatically `Unable to add channel domain to Firebase Auth`
|
120 |
+
- CHANNEL_ID = 0-2-0
|
121 |
+
- TARGET_NAME = 02-2023-release
|
122 |
+
- PROJECT_ALIAS = dev
|
123 |
``` bash
|
124 |
+
firebase hosting:channel:deploy 0-2-0 --only 02-2023-release -P dev
|
125 |
```
|
126 |
+
|
127 |
+
|
128 |
+
### 3 - Deploy and test new version of the site
|
129 |
+
> Deploy the new version of the site
|
130 |
+
- TARGET_NAME = 02-2023-release
|
131 |
+
- PROJECT_ALIAS = dev
|
132 |
+
``` bash
|
133 |
+
firebase deploy --only hosting:02-2023-release -P dev
|
134 |
+
```
|
135 |
+
|
136 |
+
> Go to the preview channel and test it
|
137 |
+
[https://dev-ionic-6-full-app.web.app]
|
138 |
+
|
139 |
+
|
140 |
+
---
|
141 |
+
|
142 |
+
## LIVE (prod)
|
143 |
+
After you test the new version of the site, you can deploy to the production site (elite-ionic-6-full-app)
|
144 |
+
|
145 |
+
|
146 |
+
### 1 - Create new target
|
147 |
+
> This updates the `.firebaserc` file
|
148 |
+
|
149 |
+
- TARGET_NAME = 02-2023-release
|
150 |
+
- RESOURCE_IDENTIFIER (the SITE_ID) = elite-ionic-6-full-app
|
151 |
+
- PROJECT_ALIAS = elite
|
152 |
+
``` bash
|
153 |
+
firebase target:apply hosting 02-2023-release elite-ionic-6-full-app -P elite
|
154 |
+
```
|
155 |
+
|
156 |
+
|
157 |
+
### 2 - Deploy and test new version of the site
|
158 |
+
> Deploy the new version of the site
|
159 |
+
- TARGET_NAME = 02-2023-release
|
160 |
+
- PROJECT_ALIAS = elite
|
161 |
+
``` bash
|
162 |
+
firebase deploy --only hosting:02-2023-release -P elite
|
163 |
+
```
|
164 |
+
|
165 |
+
> Go to the preview channel and test it
|
166 |
+
[https://elite-ionic-6-full-app.web.app]
|
@@ -22,7 +22,6 @@ Run `npm run dev:ssr`
|
|
22 |
In production, run `npm run build:ssr && npm run serve:ssr`
|
23 |
|
24 |
### To test the app as a Native App
|
25 |
-
|
26 |
This project uses [Capacitor](https://capacitor.ionicframework.com/docs/) (spiritual successor to Cordova).
|
27 |
|
28 |
[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.
|
@@ -35,8 +34,30 @@ The Capacitor workflow involves a few consistent tasks:
|
|
35 |
- [Open your Native IDE](https://capacitor.ionicframework.com/docs/basics/workflow/#3-open-your-native-ide)
|
36 |
- [Periodic Maintenance](https://capacitor.ionicframework.com/docs/basics/workflow/#4-periodic-maintenance)
|
37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
#### iOS Platform
|
39 |
-
This app has an
|
40 |
Read how to [build this app for iOS](https://capacitor.ionicframework.com/docs/basics/building-your-app#ios).
|
41 |
|
42 |
#### Android Platform
|
@@ -44,7 +65,7 @@ This app has an android folder which contains the Android native app.
|
|
44 |
Read how to [build this app for Android](https://capacitor.ionicframework.com/docs/basics/building-your-app#android).
|
45 |
|
46 |
### Want to use Cordova?
|
47 |
-
The
|
48 |
|
49 |
## Support
|
50 |
Drop us a line to contact@ionicthemes.com
|
22 |
In production, run `npm run build:ssr && npm run serve:ssr`
|
23 |
|
24 |
### To test the app as a Native App
|
|
|
25 |
This project uses [Capacitor](https://capacitor.ionicframework.com/docs/) (spiritual successor to Cordova).
|
26 |
|
27 |
[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.
|
34 |
- [Open your Native IDE](https://capacitor.ionicframework.com/docs/basics/workflow/#3-open-your-native-ide)
|
35 |
- [Periodic Maintenance](https://capacitor.ionicframework.com/docs/basics/workflow/#4-periodic-maintenance)
|
36 |
|
37 |
+
#### Building your web code
|
38 |
+
> Once you are ready to test your web app on a mobile device, you'll need to build your web app for distribution.
|
39 |
+
|
40 |
+
Run `ionic build`
|
41 |
+
|
42 |
+
#### Syncing your web code to your Capacitor project
|
43 |
+
> Once your web code has been built for distribution, you will need to push your web code to the web native Capacitor application.
|
44 |
+
|
45 |
+
Run `npx cap sync`
|
46 |
+
|
47 |
+
> 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.
|
48 |
+
|
49 |
+
#### Testing your Capacitor app
|
50 |
+
> Once you've synced over your web bundle to your native project, it is time to test your application on a mobile device.
|
51 |
+
|
52 |
+
Run `npx cap run ios` and `npx cap run android`
|
53 |
+
|
54 |
+
##### Open your Native IDE
|
55 |
+
> If you'd like more control over your native project you can quickly open the native IDEs using the Capacitor CLI.
|
56 |
+
|
57 |
+
Run `npx cap open ios` and `npx cap open android`
|
58 |
+
|
59 |
#### iOS Platform
|
60 |
+
This app has an iOS folder which contains the iOS native app.
|
61 |
Read how to [build this app for iOS](https://capacitor.ionicframework.com/docs/basics/building-your-app#ios).
|
62 |
|
63 |
#### Android Platform
|
65 |
Read how to [build this app for Android](https://capacitor.ionicframework.com/docs/basics/building-your-app#android).
|
66 |
|
67 |
### Want to use Cordova?
|
68 |
+
The ELITE 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
|
69 |
|
70 |
## Support
|
71 |
Drop us a line to contact@ionicthemes.com
|
@@ -42,6 +42,11 @@ ng generate module visualizations/charts
|
|
42 |
ng generate module visualizations/charts/charts-shared --flat
|
43 |
```
|
44 |
|
|
|
|
|
|
|
|
|
|
|
45 |
#### Visualizations / Charts / Custom Doughnut Chart
|
46 |
``` bash
|
47 |
ng generate component visualizations/charts/custom-doughnut-chart --module visualizations/charts/charts-shared --export true --skip-tests --style scss
|
42 |
ng generate module visualizations/charts/charts-shared --flat
|
43 |
```
|
44 |
|
45 |
+
#### Visualizations / Charts / Custom Progress Bar
|
46 |
+
``` bash
|
47 |
+
ng generate directive visualizations/charts/custom-progress-bar --module visualizations/charts/charts-shared --export true --skip-tests
|
48 |
+
```
|
49 |
+
|
50 |
#### Visualizations / Charts / Custom Doughnut Chart
|
51 |
``` bash
|
52 |
ng generate component visualizations/charts/custom-doughnut-chart --module visualizations/charts/charts-shared --export true --skip-tests --style scss
|
@@ -1,4 +1,50 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
``` bash
|
3 |
ng update @ionic/angular@latest @ionic/angular-server@latest --create-commits
|
4 |
```
|
@@ -7,3 +53,186 @@ ng update @ionic/angular@latest @ionic/angular-server@latest --create-commits
|
|
7 |
``` bash
|
8 |
ng update @ionic/angular-toolkit @ionic/cli --create-commits
|
9 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
> CapacitorHttp is now part of the official plugins @capacitor/core
|
32 |
+
``` bash
|
33 |
+
npm uninstall --save @capacitor-community/http
|
34 |
+
```
|
35 |
+
|
36 |
+
> Make sure you run this command after updating the Capacitor plugins
|
37 |
+
``` bash
|
38 |
+
npx cap sync
|
39 |
+
```
|
40 |
+
|
41 |
+
|
42 |
+
---
|
43 |
+
|
44 |
+
|
45 |
+
## Ionic 6.5.0
|
46 |
+
|
47 |
+
### Update to Ionic 6.5.0
|
48 |
``` bash
|
49 |
ng update @ionic/angular@latest @ionic/angular-server@latest --create-commits
|
50 |
```
|
53 |
``` bash
|
54 |
ng update @ionic/angular-toolkit @ionic/cli --create-commits
|
55 |
```
|
56 |
+
|
57 |
+
|
58 |
+
--
|
59 |
+
|
60 |
+
|
61 |
+
## Angular 15
|
62 |
+
|
63 |
+
> Make sure the Angular CLI is up-to-date
|
64 |
+
``` bash
|
65 |
+
npm install -g @angular/cli
|
66 |
+
```
|
67 |
+
|
68 |
+
### Update from Angular 13 to Angular 14
|
69 |
+
> We should update first to Angular 14 and then to Angular 15
|
70 |
+
``` bash
|
71 |
+
ng update @nguniversal/express-engine@14 @angular/cdk@14 @nguniversal/builders@14 @angular-eslint/schematics@14 @angular/core@14 @angular/cli@14 --create-commits
|
72 |
+
```
|
73 |
+
|
74 |
+
### Update from Angular 14 to Angular 15
|
75 |
+
> Then we can update Angular 15
|
76 |
+
``` bash
|
77 |
+
ng update @angular/fire @nguniversal/express-engine@15 @angular/cdk@15 @nguniversal/builders@15 @angular-eslint/schematics@15 @angular/core@15 @angular/cli@15 --create-commits
|
78 |
+
```
|
79 |
+
|
80 |
+
|
81 |
+
## Other dependencies
|
82 |
+
> First check outdated dependencies by running this command
|
83 |
+
``` bash
|
84 |
+
npm outdated --depth=0 --long
|
85 |
+
```
|
86 |
+
|
87 |
+
### Update dependencies
|
88 |
+
``` bash
|
89 |
+
npm install --save @videogular/ngx-videogular@7
|
90 |
+
npm install --save @swimlane/ngx-charts@20.1.2
|
91 |
+
npm install --save firebase
|
92 |
+
npm install --save core-js
|
93 |
+
npm install --save date-fns
|
94 |
+
npm install --save dayjs
|
95 |
+
npm install --save google-libphonenumber
|
96 |
+
npm install --save rxjs@7.8.0
|
97 |
+
npm install --save swiper
|
98 |
+
npm install --save tslib
|
99 |
+
npm install --save zone.js
|
100 |
+
npm install --save express
|
101 |
+
```
|
102 |
+
|
103 |
+
### Update dev dependencies
|
104 |
+
``` bash
|
105 |
+
npm install --save-dev @commitlint/cli @commitlint/config-angular
|
106 |
+
npm install --save-dev @types/express @types/node
|
107 |
+
npm install --save-dev @webcomponents/webcomponentsjs
|
108 |
+
npm install --save-dev ts-node
|
109 |
+
```
|
110 |
+
|
111 |
+
### Remove Bootstrap
|
112 |
+
> We did some changes and is no longer needed as a dependency for the template
|
113 |
+
``` bash
|
114 |
+
npm uninstall --save @ng-bootstrap/ng-bootstrap bootstrap @popperjs/core
|
115 |
+
```
|
116 |
+
|
117 |
+
|
118 |
+
# 2023-02-01 Updated dependencies list
|
119 |
+
> Check by running this command:
|
120 |
+
``` bash
|
121 |
+
npm list --depth=0
|
122 |
+
```
|
123 |
+
|
124 |
+
- @angular-devkit/architect@0.1501.2
|
125 |
+
- @angular-devkit/build-angular@15.1.2
|
126 |
+
- @angular-devkit/core@15.1.2
|
127 |
+
- @angular-devkit/schematics@15.1.2
|
128 |
+
- @angular-eslint/builder@15.2.0
|
129 |
+
- @angular-eslint/eslint-plugin@15.2.0
|
130 |
+
- @angular-eslint/eslint-plugin-template@15.2.0
|
131 |
+
- @angular-eslint/schematics@15.2.0
|
132 |
+
- @angular-eslint/template-parser@15.2.0
|
133 |
+
- @angular/animations@15.1.1
|
134 |
+
- @angular/cdk@15.1.1
|
135 |
+
- @angular/cli@15.1.2
|
136 |
+
- @angular/common@15.1.1
|
137 |
+
- @angular/compiler@15.1.1
|
138 |
+
- @angular/compiler-cli@15.1.1
|
139 |
+
- @angular/core@15.1.1
|
140 |
+
- @angular/fire@7.5.0
|
141 |
+
- @angular/forms@15.1.1
|
142 |
+
- @angular/language-service@15.1.1
|
143 |
+
- @angular/localize@15.1.1
|
144 |
+
- @angular/platform-browser@15.1.1
|
145 |
+
- @angular/platform-browser-dynamic@15.1.1
|
146 |
+
- @angular/platform-server@15.1.1
|
147 |
+
- @angular/router@15.1.1
|
148 |
+
- @angular/service-worker@15.1.1
|
149 |
+
- @capacitor-firebase/authentication@1.3.0
|
150 |
+
- @capacitor/android@4.6.2
|
151 |
+
- @capacitor/app@4.1.1
|
152 |
+
- @capacitor/cli@4.6.2
|
153 |
+
- @capacitor/core@4.6.2
|
154 |
+
- @capacitor/geolocation@4.1.0
|
155 |
+
- @capacitor/haptics@4.1.0
|
156 |
+
- @capacitor/ios@4.6.2
|
157 |
+
- @capacitor/keyboard@4.1.1
|
158 |
+
- @capacitor/share@4.1.0
|
159 |
+
- @capacitor/splash-screen@4.1.3
|
160 |
+
- @capacitor/status-bar@4.1.1
|
161 |
+
- @commitlint/cli@17.4.2
|
162 |
+
- @commitlint/config-angular@17.4.2
|
163 |
+
- @ionic/angular@6.5.0
|
164 |
+
- @ionic/angular-server@6.5.0
|
165 |
+
- @ionic/angular-toolkit@7.0.0
|
166 |
+
- @ionic/cli@6.20.8
|
167 |
+
- @nguniversal/builders@15.1.0
|
168 |
+
- @nguniversal/express-engine@15.1.0
|
169 |
+
- @ngx-translate/core@14.0.0
|
170 |
+
- @ngx-translate/http-loader@7.0.0
|
171 |
+
- @swimlane/ngx-charts@20.1.2
|
172 |
+
- @types/core-js@2.5.5
|
173 |
+
- @types/express@4.17.15
|
174 |
+
- @types/googlemaps@3.43.3
|
175 |
+
- @types/node@17.0.45
|
176 |
+
- @typescript-eslint/eslint-plugin@5.48.2
|
177 |
+
- @typescript-eslint/parser@5.48.2
|
178 |
+
- @videogular/ngx-videogular@7.0.1
|
179 |
+
- @webcomponents/webcomponentsjs@2.7.0
|
180 |
+
- angular-pipes@10.0.0
|
181 |
+
- cordova-res@0.15.4
|
182 |
+
- core-js@3.27.2
|
183 |
+
- date-fns@2.29.3
|
184 |
+
- dayjs@1.11.7
|
185 |
+
- eslint@8.32.0
|
186 |
+
- express@4.18.2
|
187 |
+
- firebase@9.16.0
|
188 |
+
- google-libphonenumber@3.2.31
|
189 |
+
- husky@4.3.8
|
190 |
+
- jetifier@2.0.0
|
191 |
+
- mobile-detect@1.4.5
|
192 |
+
- rxjs@7.8.0
|
193 |
+
- swiper@8.4.6
|
194 |
+
- ts-node@10.9.1
|
195 |
+
- tslib@2.4.1
|
196 |
+
- typescript@4.8.4
|
197 |
+
- zone.js@0.11.8
|
198 |
+
|
199 |
+
|
200 |
+
---
|
201 |
+
|
202 |
+
|
203 |
+
# Tags
|
204 |
+
To create an annotated tag, enter the following:
|
205 |
+
``` bash
|
206 |
+
git tag -a 02-2023_elite-update -m '02-2023 ELITE update'
|
207 |
+
```
|
208 |
+
Then run:
|
209 |
+
``` bash
|
210 |
+
git push origin --tags
|
211 |
+
```
|
212 |
+
|
213 |
+
|
214 |
+
---
|
215 |
+
|
216 |
+
|
217 |
+
# Miscellaneous
|
218 |
+
> When updating an existing project you may want to get a specific file from another branch
|
219 |
+
|
220 |
+
### Cherry-pick files from another git branch
|
221 |
+
> Get just one file from another branch
|
222 |
+
``` bash
|
223 |
+
git checkout elite-release-1 -- src/app/activity/activities-filter-modal/activities-filter.modal.html
|
224 |
+
git checkout elite-release-1 -- src/app/activity/activities-filter-modal/activities-filter.modal.scss
|
225 |
+
git checkout elite-release-1 -- src/app/app.component.html
|
226 |
+
git checkout elite-release-1 -- src/app/app.component.ts
|
227 |
+
git checkout elite-release-1 -- src/app/showcase/showcase.module.ts
|
228 |
+
git checkout elite-release-1 -- src/app/visualizations/charts/custom-doughnut-chart/custom-doughnut-chart.component.html
|
229 |
+
git checkout elite-release-1 -- src/app/visualizations/charts/custom-doughnut-chart/custom-doughnut-chart.component.ts
|
230 |
+
git checkout elite-release-1 -- src/app/visualizations/charts/custom-line-chart/custom-line-chart.component.html
|
231 |
+
git checkout elite-release-1 -- src/app/visualizations/charts/custom-line-chart/custom-line-chart.component.scss
|
232 |
+
git checkout elite-release-1 -- src/app/visualizations/charts/custom-line-chart/custom-line-chart.component.ts
|
233 |
+
git checkout elite-release-1 -- src/app/visualizations/visualizations-routing.module.ts
|
234 |
+
git checkout elite-release-1 -- src/app/visualizations/visualizations.module.ts
|
235 |
+
git checkout elite-release-1 -- firebase.json
|
236 |
+
git checkout elite-release-1 -- FIREBASE.md
|
237 |
+
git checkout elite-release-1 -- .firebaserc
|
238 |
+
```
|
@@ -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": {
|
@@ -186,7 +185,10 @@
|
|
186 |
}
|
187 |
},
|
188 |
"cli": {
|
189 |
-
"
|
|
|
|
|
|
|
190 |
},
|
191 |
"schematics": {
|
192 |
"@ionic/angular-toolkit:component": {
|
@@ -194,6 +196,12 @@
|
|
194 |
},
|
195 |
"@ionic/angular-toolkit:page": {
|
196 |
"styleext": "scss"
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
}
|
198 |
}
|
199 |
-
}
|
1 |
{
|
2 |
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
|
3 |
"version": 1,
|
|
|
4 |
"newProjectRoot": "projects",
|
5 |
"projects": {
|
6 |
"app": {
|
185 |
}
|
186 |
},
|
187 |
"cli": {
|
188 |
+
"schematicCollections": [
|
189 |
+
"@ionic/angular-toolkit",
|
190 |
+
"@angular-eslint/schematics"
|
191 |
+
]
|
192 |
},
|
193 |
"schematics": {
|
194 |
"@ionic/angular-toolkit:component": {
|
196 |
},
|
197 |
"@ionic/angular-toolkit:page": {
|
198 |
"styleext": "scss"
|
199 |
+
},
|
200 |
+
"@angular-eslint/schematics:application": {
|
201 |
+
"setParserOptionsProject": true
|
202 |
+
},
|
203 |
+
"@angular-eslint/schematics:library": {
|
204 |
+
"setParserOptionsProject": true
|
205 |
}
|
206 |
}
|
207 |
+
}
|
@@ -1,7 +1,7 @@
|
|
1 |
{
|
2 |
"hosting": [
|
3 |
{
|
4 |
-
"target": "
|
5 |
"public": "dist/app/browser",
|
6 |
"ignore": [
|
7 |
"firebase.json",
|
@@ -43,49 +43,7 @@
|
|
43 |
]
|
44 |
},
|
45 |
{
|
46 |
-
"target": "
|
47 |
-
"public": "dist/app/browser",
|
48 |
-
"ignore": [
|
49 |
-
"firebase.json",
|
50 |
-
"**/.*",
|
51 |
-
"**/node_modules/**"
|
52 |
-
],
|
53 |
-
"rewrites": [ {
|
54 |
-
"source": "**",
|
55 |
-
"destination": "/index.html"
|
56 |
-
} ],
|
57 |
-
"headers": [
|
58 |
-
{
|
59 |
-
"source": "**",
|
60 |
-
"headers": [
|
61 |
-
{
|
62 |
-
"key": "Cache-Control",
|
63 |
-
"value": "no-cache, no-store, must-revalidate"
|
64 |
-
}
|
65 |
-
]
|
66 |
-
},
|
67 |
-
{
|
68 |
-
"source": "**/*.@(jpg|jpeg|gif|png|svg|webp|js|css|eot|otf|ttf|ttc|woff|font.css)",
|
69 |
-
"headers": [
|
70 |
-
{
|
71 |
-
"key": "Cache-Control",
|
72 |
-
"value": "no-cache"
|
73 |
-
}
|
74 |
-
]
|
75 |
-
},
|
76 |
-
{
|
77 |
-
"source": "ngsw-worker.js",
|
78 |
-
"headers": [
|
79 |
-
{
|
80 |
-
"key": "Cache-Control",
|
81 |
-
"value": "no-cache"
|
82 |
-
}
|
83 |
-
]
|
84 |
-
}
|
85 |
-
]
|
86 |
-
},
|
87 |
-
{
|
88 |
-
"target": "12-2021-release",
|
89 |
"public": "dist/app/browser",
|
90 |
"ignore": [
|
91 |
"firebase.json",
|
1 |
{
|
2 |
"hosting": [
|
3 |
{
|
4 |
+
"target": "02-2023-release",
|
5 |
"public": "dist/app/browser",
|
6 |
"ignore": [
|
7 |
"firebase.json",
|
43 |
]
|
44 |
},
|
45 |
{
|
46 |
+
"target": "11-2022-release",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
"public": "dist/app/browser",
|
48 |
"ignore": [
|
49 |
"firebase.json",
|
@@ -1,7 +1,7 @@
|
|
1 |
{
|
2 |
"name": "IonicFullApp-ELITE",
|
3 |
"description": "The most advanced and complete Mobile & PWA Ionic starter app template",
|
4 |
-
"version": "0.0
|
5 |
"author": "IonicThemes",
|
6 |
"contributors": [
|
7 |
"Dayana <dayana@ionicthemes.com>",
|
@@ -21,85 +21,82 @@
|
|
21 |
"postinstall": "jetifier"
|
22 |
},
|
23 |
"dependencies": {
|
24 |
-
"@angular/animations": "^
|
25 |
-
"@angular/cdk": "^
|
26 |
-
"@angular/common": "^
|
27 |
-
"@angular/core": "^
|
28 |
-
"@angular/fire": "^7.
|
29 |
-
"@angular/forms": "^
|
30 |
-
"@angular/localize": "^
|
31 |
-
"@angular/platform-browser": "^
|
32 |
-
"@angular/platform-browser-dynamic": "^
|
33 |
-
"@angular/platform-server": "^
|
34 |
-
"@angular/router": "^
|
35 |
-
"@angular/service-worker": "^
|
36 |
-
"@capacitor-
|
37 |
-
"@capacitor
|
38 |
-
"@capacitor/
|
39 |
-
"@capacitor/
|
40 |
-
"@capacitor/
|
41 |
-
"@capacitor/
|
42 |
-
"@capacitor/
|
43 |
-
"@capacitor/
|
44 |
-
"@capacitor/
|
45 |
-
"@capacitor/share": "^
|
46 |
-
"@capacitor/splash-screen": "^1.
|
47 |
-
"@capacitor/status-bar": "^
|
48 |
-
"@ionic/angular": "^6.
|
49 |
-
"@ionic/angular-server": "^6.
|
50 |
-
"@
|
51 |
-
"@nguniversal/express-engine": "^13.0.1",
|
52 |
"@ngx-translate/core": "^14.0.0",
|
53 |
"@ngx-translate/http-loader": "^7.0.0",
|
54 |
-
"@
|
55 |
-
"@
|
56 |
-
"@videogular/ngx-videogular": "^5.0.1",
|
57 |
"angular-pipes": "^10.0.0",
|
58 |
-
"
|
59 |
-
"
|
60 |
-
"
|
61 |
-
"
|
62 |
-
"
|
63 |
-
"
|
64 |
-
"google-libphonenumber": "^3.2.27",
|
65 |
"jetifier": "^2.0.0",
|
66 |
"mobile-detect": "^1.4.5",
|
67 |
-
"rxjs": "^7.
|
68 |
-
"swiper": "^8.
|
69 |
-
"tslib": "^2.
|
70 |
-
"zone.js": "
|
71 |
},
|
72 |
"devDependencies": {
|
73 |
-
"@angular-devkit/architect": "0.
|
74 |
-
"@angular-devkit/build-angular": "
|
75 |
-
"@angular-devkit/core": "^
|
76 |
-
"@angular-devkit/schematics": "^
|
77 |
-
"@angular-eslint/builder": "
|
78 |
-
"@angular-eslint/eslint-plugin": "
|
79 |
-
"@angular-eslint/eslint-plugin-template": "
|
80 |
-
"@angular-eslint/schematics": "
|
81 |
-
"@angular-eslint/template-parser": "
|
82 |
-
"@angular/cli": "
|
83 |
-
"@angular/compiler": "^
|
84 |
-
"@angular/compiler-cli": "^
|
85 |
-
"@angular/language-service": "
|
86 |
-
"@capacitor/cli": "^
|
87 |
-
"@commitlint/cli": "^17.
|
88 |
-
"@commitlint/config-angular": "^17.
|
89 |
"@ionic/angular-toolkit": "^7.0.0",
|
90 |
-
"@ionic/cli": "6.20.
|
91 |
-
"@nguniversal/builders": "^
|
92 |
"@types/core-js": "^2.5.5",
|
93 |
-
"@types/express": "^4.17.
|
94 |
"@types/googlemaps": "^3.39.2",
|
95 |
-
"@types/node": "^17.0.
|
96 |
-
"@typescript-eslint/eslint-plugin": "^5.
|
97 |
-
"@typescript-eslint/parser": "^5.
|
98 |
-
"@webcomponents/webcomponentsjs": "^2.
|
99 |
"cordova-res": "0.15.4",
|
100 |
-
"eslint": "^8.
|
101 |
"husky": "^4.3.8",
|
102 |
-
"ts-node": "^10.
|
103 |
-
"typescript": "~4.
|
104 |
}
|
105 |
}
|
1 |
{
|
2 |
"name": "IonicFullApp-ELITE",
|
3 |
"description": "The most advanced and complete Mobile & PWA Ionic starter app template",
|
4 |
+
"version": "0.2.0",
|
5 |
"author": "IonicThemes",
|
6 |
"contributors": [
|
7 |
"Dayana <dayana@ionicthemes.com>",
|
21 |
"postinstall": "jetifier"
|
22 |
},
|
23 |
"dependencies": {
|
24 |
+
"@angular/animations": "^15.1.1",
|
25 |
+
"@angular/cdk": "^15.1.1",
|
26 |
+
"@angular/common": "^15.1.1",
|
27 |
+
"@angular/core": "^15.1.1",
|
28 |
+
"@angular/fire": "^7.5.0",
|
29 |
+
"@angular/forms": "^15.1.1",
|
30 |
+
"@angular/localize": "^15.1.1",
|
31 |
+
"@angular/platform-browser": "^15.1.1",
|
32 |
+
"@angular/platform-browser-dynamic": "^15.1.1",
|
33 |
+
"@angular/platform-server": "^15.1.1",
|
34 |
+
"@angular/router": "^15.1.1",
|
35 |
+
"@angular/service-worker": "^15.1.1",
|
36 |
+
"@capacitor-firebase/authentication": "^1.3.0",
|
37 |
+
"@capacitor/android": "^4.6.2",
|
38 |
+
"@capacitor/app": "^4.0.0",
|
39 |
+
"@capacitor/core": "^4.6.2",
|
40 |
+
"@capacitor/geolocation": "^4.0.0",
|
41 |
+
"@capacitor/haptics": "^4.0.0",
|
42 |
+
"@capacitor/ios": "^4.6.2",
|
43 |
+
"@capacitor/keyboard": "^4.1.1",
|
44 |
+
"@capacitor/preferences": "^4.0.2",
|
45 |
+
"@capacitor/share": "^4.0.0",
|
46 |
+
"@capacitor/splash-screen": "^4.1.3",
|
47 |
+
"@capacitor/status-bar": "^4.0.0",
|
48 |
+
"@ionic/angular": "^6.5.0",
|
49 |
+
"@ionic/angular-server": "^6.5.0",
|
50 |
+
"@nguniversal/express-engine": "^15.1.0",
|
|
|
51 |
"@ngx-translate/core": "^14.0.0",
|
52 |
"@ngx-translate/http-loader": "^7.0.0",
|
53 |
+
"@swimlane/ngx-charts": "^20.1.2",
|
54 |
+
"@videogular/ngx-videogular": "^7.0.1",
|
|
|
55 |
"angular-pipes": "^10.0.0",
|
56 |
+
"core-js": "^3.27.2",
|
57 |
+
"date-fns": "^2.29.3",
|
58 |
+
"dayjs": "^1.11.7",
|
59 |
+
"express": "^4.18.2",
|
60 |
+
"firebase": "^9.16.0",
|
61 |
+
"google-libphonenumber": "^3.2.31",
|
|
|
62 |
"jetifier": "^2.0.0",
|
63 |
"mobile-detect": "^1.4.5",
|
64 |
+
"rxjs": "^7.8.0",
|
65 |
+
"swiper": "^8.4.6",
|
66 |
+
"tslib": "^2.4.1",
|
67 |
+
"zone.js": "^0.11.8"
|
68 |
},
|
69 |
"devDependencies": {
|
70 |
+
"@angular-devkit/architect": "0.1501.2",
|
71 |
+
"@angular-devkit/build-angular": "^15.1.2",
|
72 |
+
"@angular-devkit/core": "^15.1.2",
|
73 |
+
"@angular-devkit/schematics": "^15.1.2",
|
74 |
+
"@angular-eslint/builder": "15.2.0",
|
75 |
+
"@angular-eslint/eslint-plugin": "15.2.0",
|
76 |
+
"@angular-eslint/eslint-plugin-template": "15.2.0",
|
77 |
+
"@angular-eslint/schematics": "15.2.0",
|
78 |
+
"@angular-eslint/template-parser": "15.2.0",
|
79 |
+
"@angular/cli": "^15.1.2",
|
80 |
+
"@angular/compiler": "^15.1.1",
|
81 |
+
"@angular/compiler-cli": "^15.1.1",
|
82 |
+
"@angular/language-service": "^15.1.1",
|
83 |
+
"@capacitor/cli": "^4.6.2",
|
84 |
+
"@commitlint/cli": "^17.4.2",
|
85 |
+
"@commitlint/config-angular": "^17.4.2",
|
86 |
"@ionic/angular-toolkit": "^7.0.0",
|
87 |
+
"@ionic/cli": "6.20.8",
|
88 |
+
"@nguniversal/builders": "^15.1.0",
|
89 |
"@types/core-js": "^2.5.5",
|
90 |
+
"@types/express": "^4.17.15",
|
91 |
"@types/googlemaps": "^3.39.2",
|
92 |
+
"@types/node": "^17.0.45",
|
93 |
+
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
94 |
+
"@typescript-eslint/parser": "^5.43.0",
|
95 |
+
"@webcomponents/webcomponentsjs": "^2.7.0",
|
96 |
"cordova-res": "0.15.4",
|
97 |
+
"eslint": "^8.28.0",
|
98 |
"husky": "^4.3.8",
|
99 |
+
"ts-node": "^10.9.1",
|
100 |
+
"typescript": "~4.8.4"
|
101 |
}
|
102 |
}
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, Input, OnInit } from '@angular/core';
|
2 |
-
import {
|
3 |
import { ModalController } from '@ionic/angular';
|
4 |
|
5 |
import { ActivityCategory } from '../activity.data';
|
@@ -14,7 +14,7 @@ import { ActivityService } from '../activity.service';
|
|
14 |
export class ActivitiesFilterModalComponent implements OnInit {
|
15 |
currentSelectedActivities: Array<ActivityCategory>;
()
|
16 |
|
17 |
-
filterActivitiesForm:
|
18 |
|
19 |
activityCategories: Array<ActivityCategory>;
|
20 |
|
@@ -30,12 +30,12 @@ export class ActivitiesFilterModalComponent implements OnInit {
|
|
30 |
|
31 |
this.currentSelectedActivities.forEach(
|
32 |
(c: { name: string; title: string; value: boolean }) => {
|
33 |
-
activitiesControls[c.name] = new
|
34 |
}
|
35 |
);
|
36 |
|
37 |
-
this.filterActivitiesForm = new
|
38 |
-
activities: new
|
39 |
});
|
40 |
}
|
41 |
|
@@ -44,7 +44,7 @@ export class ActivitiesFilterModalComponent implements OnInit {
|
|
44 |
}
|
45 |
|
46 |
selectAllFilters(): void {
|
47 |
-
const activities = this.filterActivitiesForm.get('activities') as
|
48 |
|
49 |
for (const field in activities.controls) {
|
50 |
activities.get(field).setValue(true);
|
@@ -52,7 +52,7 @@ export class ActivitiesFilterModalComponent implements OnInit {
|
|
52 |
}
|
53 |
|
54 |
deselectAllFilters(): void {
|
55 |
-
const activities = this.filterActivitiesForm.get('activities') as
|
56 |
|
57 |
for (const field in activities.controls) {
|
58 |
activities.get(field).setValue(false);
|
@@ -60,7 +60,7 @@ export class ActivitiesFilterModalComponent implements OnInit {
|
|
60 |
}
|
61 |
|
62 |
applyFilters(): void {
|
63 |
-
const activitiesFilterControlGroup = this.filterActivitiesForm.get('activities') as
|
64 |
|
65 |
for (const activityControl in activitiesFilterControlGroup.controls) {
|
66 |
this.activityCategories.map(
|
1 |
import { Component, Input, OnInit } from '@angular/core';
|
2 |
+
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
3 |
import { ModalController } from '@ionic/angular';
|
4 |
|
5 |
import { ActivityCategory } from '../activity.data';
|
14 |
export class ActivitiesFilterModalComponent implements OnInit {
|
15 |
currentSelectedActivities: Array<ActivityCategory>;
()
|
16 |
|
17 |
+
filterActivitiesForm: UntypedFormGroup;
|
18 |
|
19 |
activityCategories: Array<ActivityCategory>;
|
20 |
|
30 |
|
31 |
this.currentSelectedActivities.forEach(
|
32 |
(c: { name: string; title: string; value: boolean }) => {
|
33 |
+
activitiesControls[c.name] = new UntypedFormControl(c.value);
|
34 |
}
|
35 |
);
|
36 |
|
37 |
+
this.filterActivitiesForm = new UntypedFormGroup({
|
38 |
+
activities: new UntypedFormGroup(activitiesControls),
|
39 |
});
|
40 |
}
|
41 |
|
44 |
}
|
45 |
|
46 |
selectAllFilters(): void {
|
47 |
+
const activities = this.filterActivitiesForm.get('activities') as UntypedFormGroup;
|
48 |
|
49 |
for (const field in activities.controls) {
|
50 |
activities.get(field).setValue(true);
|
52 |
}
|
53 |
|
54 |
deselectAllFilters(): void {
|
55 |
+
const activities = this.filterActivitiesForm.get('activities') as UntypedFormGroup;
|
56 |
|
57 |
for (const field in activities.controls) {
|
58 |
activities.get(field).setValue(false);
|
60 |
}
|
61 |
|
62 |
applyFilters(): void {
|
63 |
+
const activitiesFilterControlGroup = this.filterActivitiesForm.get('activities') as UntypedFormGroup;
|
64 |
|
65 |
for (const activityControl in activitiesFilterControlGroup.controls) {
|
66 |
this.activityCategories.map(
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component } from '@angular/core';
|
2 |
-
import { AbstractControl,
|
3 |
import { IonRouterOutlet, ModalController } from '@ionic/angular';
|
4 |
|
5 |
import { Subscription, throttleTime } from 'rxjs';
|
@@ -15,7 +15,7 @@ import { ActivityService } from './activity.service';
|
|
15 |
styleUrls: ['./styles/activity.page.scss']
|
16 |
})
|
17 |
export class ActivityPage {
|
18 |
-
filterActivitiesForm:
|
19 |
|
20 |
activitiesChangesSubscription: Subscription;
|
21 |
|
@@ -42,12 +42,12 @@ export class ActivityPage {
|
|
42 |
|
43 |
this.activityCategories.forEach(
|
44 |
(c: { name: string; title: string; value: boolean }) => {
|
45 |
-
activitiesControls[c.name] = new
|
46 |
}
|
47 |
);
|
48 |
|
49 |
-
this.filterActivitiesForm = new
|
50 |
-
activities: new
|
51 |
});
|
52 |
|
53 |
// ? Check what activity we should display
|
@@ -58,7 +58,7 @@ export class ActivityPage {
|
|
58 |
}
|
59 |
|
60 |
async presentActivitiesFilterModal() {
|
61 |
-
const activitiesFilterControlGroup = this.filterActivitiesForm.get('activities') as
|
62 |
|
63 |
for (const activityControl in activitiesFilterControlGroup.controls) {
|
64 |
this.activityCategories.map(
|
1 |
import { Component } from '@angular/core';
|
2 |
+
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
3 |
import { IonRouterOutlet, ModalController } from '@ionic/angular';
|
4 |
|
5 |
import { Subscription, throttleTime } from 'rxjs';
|
15 |
styleUrls: ['./styles/activity.page.scss']
|
16 |
})
|
17 |
export class ActivityPage {
|
18 |
+
filterActivitiesForm: UntypedFormGroup;
|
19 |
|
20 |
activitiesChangesSubscription: Subscription;
|
21 |
|
42 |
|
43 |
this.activityCategories.forEach(
|
44 |
(c: { name: string; title: string; value: boolean }) => {
|
45 |
+
activitiesControls[c.name] = new UntypedFormControl(c.value);
|
46 |
}
|
47 |
);
|
48 |
|
49 |
+
this.filterActivitiesForm = new UntypedFormGroup({
|
50 |
+
activities: new UntypedFormGroup(activitiesControls),
|
51 |
});
|
52 |
|
53 |
// ? Check what activity we should display
|
58 |
}
|
59 |
|
60 |
async presentActivitiesFilterModal() {
|
61 |
+
const activitiesFilterControlGroup = this.filterActivitiesForm.get('activities') as UntypedFormGroup;
|
62 |
|
63 |
for (const activityControl in activitiesFilterControlGroup.controls) {
|
64 |
this.activityCategories.map(
|
@@ -94,7 +94,7 @@ const routes: Routes = [
|
|
94 |
imports: [
|
95 |
RouterModule.forRoot(routes, {
|
96 |
// This value is required for server-side rendering to work.
|
97 |
-
initialNavigation: '
|
98 |
scrollPositionRestoration: 'enabled',
|
99 |
anchorScrolling: 'enabled'
|
100 |
})
|
94 |
imports: [
|
95 |
RouterModule.forRoot(routes, {
|
96 |
// This value is required for server-side rendering to work.
|
97 |
+
initialNavigation: 'enabledBlocking',
|
98 |
scrollPositionRestoration: 'enabled',
|
99 |
anchorScrolling: 'enabled'
|
100 |
})
|
@@ -118,6 +118,14 @@
|
|
118 |
</ion-label>
|
119 |
</ion-item>
|
120 |
</ion-menu-toggle>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
</ion-list>
|
122 |
<ion-list>
|
123 |
<ion-list-header>
|
118 |
</ion-label>
|
119 |
</ion-item>
|
120 |
</ion-menu-toggle>
|
121 |
+
<ion-menu-toggle autoHide="false">
|
122 |
+
<ion-item (click)="openTutorial()">
|
123 |
+
<ion-icon slot="start" name="school-outline"></ion-icon>
|
124 |
+
<ion-label>
|
125 |
+
Tutorial
|
126 |
+
</ion-label>
|
127 |
+
</ion-item>
|
128 |
+
</ion-menu-toggle>
|
129 |
</ion-list>
|
130 |
<ion-list>
|
131 |
<ion-list-header>
|
@@ -1,9 +1,14 @@
|
|
1 |
import { Component } from '@angular/core';
|
|
|
|
|
2 |
import { SplashScreen } from '@capacitor/splash-screen';
|
3 |
-
import {
|
|
|
4 |
import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
|
|
|
5 |
import { HistoryHelperService } from './utils/history-helper.service';
|
6 |
|
|
|
7 |
({
|
8 |
selector: 'app-root',
|
9 |
templateUrl: 'app.component.html',
|
@@ -73,11 +78,6 @@ export class AppComponent {
|
|
73 |
url: '/auth/signup',
|
74 |
ionicIcon: 'person-add-outline'
|
75 |
},
|
76 |
-
{
|
77 |
-
title: 'Tutorial',
|
78 |
-
url: '/walkthrough',
|
79 |
-
ionicIcon: 'school-outline'
|
80 |
-
},
|
81 |
{
|
82 |
title: 'Getting Started',
|
83 |
url: '/getting-started',
|
@@ -96,7 +96,7 @@ export class AppComponent {
|
|
96 |
constructor(
|
97 |
public translate: TranslateService,
|
98 |
public historyHelper: HistoryHelperService,
|
99 |
-
private
|
100 |
) {
|
101 |
this.initializeApp();
|
102 |
this.setLanguage();
|
@@ -110,7 +110,7 @@ export class AppComponent {
|
|
110 |
}
|
111 |
}
|
112 |
|
113 |
-
setLanguage() {
|
114 |
// this language will be used as a fallback when a translation isn't found in the current language
|
115 |
this.translate.setDefaultLang('en');
|
116 |
|
@@ -124,4 +124,12 @@ export class AppComponent {
|
|
124 |
// });
|
125 |
}
|
126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
}
|
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 |
import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
|
8 |
+
|
9 |
import { HistoryHelperService } from './utils/history-helper.service';
|
10 |
|
11 |
+
|
12 |
({
|
13 |
selector: 'app-root',
|
14 |
templateUrl: 'app.component.html',
|
78 |
url: '/auth/signup',
|
79 |
ionicIcon: 'person-add-outline'
|
80 |
},
|
|
|
|
|
|
|
|
|
|
|
81 |
{
|
82 |
title: 'Getting Started',
|
83 |
url: '/getting-started',
|
96 |
constructor(
|
97 |
public translate: TranslateService,
|
98 |
public historyHelper: HistoryHelperService,
|
99 |
+
private router: Router
|
100 |
) {
|
101 |
this.initializeApp();
|
102 |
this.setLanguage();
|
110 |
}
|
111 |
}
|
112 |
|
113 |
+
public setLanguage(): void {
|
114 |
// this language will be used as a fallback when a translation isn't found in the current language
|
115 |
this.translate.setDefaultLang('en');
|
116 |
|
124 |
// });
|
125 |
}
|
126 |
|
127 |
+
public openTutorial(): void {
|
128 |
+
// save key to mark the walkthrough as NOT visited because the user wants to check it out
|
129 |
+
Preferences.set({
|
130 |
+
key: 'visitedWalkthrough',
|
131 |
+
value: 'false'
|
132 |
+
});
|
133 |
+
this.router.navigateByUrl('walkthrough');
|
134 |
+
}
|
135 |
}
|
@@ -16,7 +16,6 @@ import { HttpClientModule, HttpClient } from '@angular/common/http';
|
|
16 |
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
17 |
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
18 |
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
19 |
-
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
20 |
|
21 |
|
22 |
export function createTranslateLoader(http: HttpClient) {
|
@@ -41,8 +40,7 @@ export function createTranslateLoader(http: HttpClient) {
|
|
41 |
useFactory: (createTranslateLoader),
|
42 |
deps: [HttpClient]
|
43 |
}
|
44 |
-
})
|
45 |
-
NgbModule
|
46 |
],
|
47 |
providers: [
|
48 |
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
16 |
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
17 |
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
18 |
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
|
|
19 |
|
20 |
|
21 |
export function createTranslateLoader(http: HttpClient) {
|
40 |
useFactory: (createTranslateLoader),
|
41 |
deps: [HttpClient]
|
42 |
}
|
43 |
+
})
|
|
|
44 |
],
|
45 |
providers: [
|
46 |
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
@@ -87,7 +87,7 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
|
|
87 |
}
|
88 |
|
89 |
constructor(
|
90 |
-
PLATFORM_ID) private platformId: object
|
91 |
) { }
|
92 |
|
93 |
ngOnInit(): void {
|
87 |
}
|
88 |
|
89 |
constructor(
|
90 |
+
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 |
}
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component } from '@angular/core';
|
2 |
-
import {
|
3 |
|
4 |
import { ContactsService } from './contacts.service';
|
5 |
import { ContactsGroup } from './contacts.data';
|
@@ -11,15 +11,15 @@ import { ContactsGroup } from './contacts.data';
|
|
11 |
styleUrls: ['./styles/contacts.page.scss']
|
12 |
})
|
13 |
export class ContactsPage {
|
14 |
-
contactsFiltersForm:
|
15 |
|
16 |
currentContactsFilter: string;
|
17 |
|
18 |
filteredContacts: Array<ContactsGroup>;
|
19 |
|
20 |
constructor(private contactsService: ContactsService) {
|
21 |
-
this.contactsFiltersForm = new
|
22 |
-
contactsFilter: new
|
23 |
});
|
24 |
|
25 |
// ? Check what template we should use to render the contacts
|
1 |
import { Component } from '@angular/core';
|
2 |
+
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
|
3 |
|
4 |
import { ContactsService } from './contacts.service';
|
5 |
import { ContactsGroup } from './contacts.data';
|
11 |
styleUrls: ['./styles/contacts.page.scss']
|
12 |
})
|
13 |
export class ContactsPage {
|
14 |
+
contactsFiltersForm: UntypedFormGroup;
|
15 |
|
16 |
currentContactsFilter: string;
|
17 |
|
18 |
filteredContacts: Array<ContactsGroup>;
|
19 |
|
20 |
constructor(private contactsService: ContactsService) {
|
21 |
+
this.contactsFiltersForm = new UntypedFormGroup({
|
22 |
+
contactsFilter: new UntypedFormControl('birthday', Validators.required),
|
23 |
});
|
24 |
|
25 |
// ? Check what template we should use to render the contacts
|
@@ -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 |
+
()
|
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,15 +9,15 @@ 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 { DataStore } from '../../shell/data-store';
|
19 |
import { FirebaseProfileModel } from './profile/firebase-profile.model';
|
20 |
import { SignInProvider } from './firebase-auth-definitions';
|
|
|
21 |
|
22 |
|
23 |
({
|
@@ -35,6 +35,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
35 |
public route: ActivatedRoute,
|
36 |
public platform: Platform,
|
37 |
private ngZone: NgZone,
|
|
|
38 |
public loadingController: LoadingController,
|
39 |
public location: Location,
|
40 |
PLATFORM_ID) private platformId: object
(
|
@@ -90,7 +91,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
90 |
break;
|
91 |
}
|
92 |
|
93 |
-
const signInResult =
|
94 |
|
95 |
this.dismissLoading();
|
96 |
|
@@ -132,6 +133,36 @@ export class FirebaseAuthService implements OnDestroy {
|
|
132 |
this.dismissLoading();
|
133 |
}
|
134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
public async signOut(): Promise<string> {
|
136 |
const signOutPromise = new Promise<string>((resolve, reject) => {
|
137 |
// * 1. Sign out on the native layer
|
@@ -156,15 +187,15 @@ export class FirebaseAuthService implements OnDestroy {
|
|
156 |
return signOutPromise;
|
157 |
}
|
158 |
|
159 |
-
private async socialSignIn(provider:
|
160 |
this.presentLoading(provider.providerId);
|
161 |
|
162 |
let authResult: SignInResult = null;
|
163 |
|
164 |
if (this.platform.is('capacitor')) {
|
165 |
-
authResult = await this.nativeAuth(provider,
|
166 |
} else {
|
167 |
-
authResult = await this.webAuth(provider);
|
168 |
}
|
169 |
|
170 |
this.dismissLoading();
|
@@ -176,66 +207,12 @@ export class FirebaseAuthService implements OnDestroy {
|
|
176 |
}
|
177 |
}
|
178 |
|
179 |
-
private
|
180 |
-
// ? Before invoking auth provider redirect flow, add a flag to the path.
|
181 |
-
// ? The presence of the flag in the path indicates we should wait for the auth redirect to complete
|
182 |
-
this.location.replaceState(this.location.path(), 'auth-redirect=' + authProviderId, this.location.getState());
|
183 |
-
}
|
184 |
-
|
185 |
-
private clearAuthWithProvidersRedirection(): void {
|
186 |
-
// ? Remove auth-redirect param from url
|
187 |
-
this.location.replaceState(this.router.url.split('?')[0], '');
|
188 |
-
this.dismissLoading();
|
189 |
-
}
|
190 |
-
|
191 |
-
private async presentLoading(authProviderId?: string): Promise<void> {
|
192 |
-
const authProviderCapitalized = authProviderId[0].toUpperCase() + authProviderId.slice(1);
|
193 |
-
|
194 |
-
this.loadingController.create({
|
195 |
-
message: authProviderId ? 'Signing in with ' + authProviderCapitalized : 'Signing in ...',
|
196 |
-
duration: 4000
|
197 |
-
}).then((loader) => {
|
198 |
-
this.authLoader = loader;
|
199 |
-
this.authLoader.present();
|
200 |
-
});
|
201 |
-
}
|
202 |
-
|
203 |
-
private async dismissLoading(): Promise<void> {
|
204 |
-
if (this.authLoader) {
|
205 |
-
await this.authLoader.dismiss();
|
206 |
-
}
|
207 |
-
}
|
208 |
-
|
209 |
-
private async webAuth(provider: AuthProvider, scopes?: Array<string>): Promise<SignInResult> {
|
210 |
-
// ? Scopes for Firebase JS SDK auth
|
211 |
-
if (scopes) {
|
212 |
-
let providerWithScopes: any;
|
213 |
-
|
214 |
-
switch (provider.providerId) {
|
215 |
-
case SignInProvider.apple:
|
216 |
-
providerWithScopes = (provider as OAuthProvider);
|
217 |
-
break;
|
218 |
-
case SignInProvider.facebook:
|
219 |
-
providerWithScopes = (provider as FacebookAuthProvider);
|
220 |
-
break;
|
221 |
-
case SignInProvider.google:
|
222 |
-
providerWithScopes = (provider as GoogleAuthProvider);
|
223 |
-
break;
|
224 |
-
case SignInProvider.twitter:
|
225 |
-
providerWithScopes = (provider as TwitterAuthProvider);
|
226 |
-
break;
|
227 |
-
}
|
228 |
-
|
229 |
-
scopes.forEach(scope => {
|
230 |
-
providerWithScopes.addScope(scope);
|
231 |
-
});
|
232 |
-
|
233 |
-
provider = providerWithScopes;
|
234 |
-
}
|
235 |
-
|
236 |
const auth = getAuth();
|
237 |
let webAuthUserCredential: UserCredential = null;
|
238 |
|
|
|
|
|
239 |
if (this.platform.is('desktop')) {
|
240 |
webAuthUserCredential = await signInWithPopup(auth, provider);
|
241 |
} else {
|
@@ -265,33 +242,28 @@ export class FirebaseAuthService implements OnDestroy {
|
|
265 |
break;
|
266 |
}
|
267 |
|
268 |
-
return this.createSignInResult(webAuthUserCredential
|
269 |
} else {
|
270 |
return Promise.reject('null webAuthUserCredential');
|
271 |
}
|
272 |
}
|
273 |
|
274 |
-
private async nativeAuth(provider: AuthProvider,
|
275 |
let nativeAuthResult: SignInResult = null;
|
276 |
|
277 |
-
// ? Scopes for Firebase native SDK (iOS and Android)
|
278 |
-
// TODO: Scopes for Firebase native SDK auth is a work in progress yet
|
279 |
-
// (see: https://github.com/robingenz/capacitor-firebase/issues/32)
|
280 |
-
|
281 |
-
|
282 |
// * 1. Sign in on the native layer
|
283 |
switch (provider.providerId) {
|
284 |
case SignInProvider.apple:
|
285 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithApple();
|
286 |
break;
|
287 |
case SignInProvider.facebook:
|
288 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithFacebook();
|
289 |
break;
|
290 |
case SignInProvider.google:
|
291 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithGoogle();
|
292 |
break;
|
293 |
case SignInProvider.twitter:
|
294 |
-
nativeAuthResult = await FirebaseAuthentication.signInWithTwitter();
|
295 |
break;
|
296 |
}
|
297 |
|
@@ -314,11 +286,17 @@ export class FirebaseAuthService implements OnDestroy {
|
|
314 |
);
|
315 |
break;
|
316 |
case SignInProvider.google:
|
317 |
-
nativeCredential = GoogleAuthProvider.credential(
|
|
|
|
|
|
|
318 |
break;
|
319 |
case SignInProvider.twitter:
|
320 |
try {
|
321 |
-
nativeCredential = TwitterAuthProvider.credential(
|
|
|
|
|
|
|
322 |
break;
|
323 |
} catch (error) {
|
324 |
console.error(error);
|
@@ -328,7 +306,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
328 |
// * 2. Sign in on the web layer using the access token we got from the native sign in
|
329 |
const webAuthResult = await signInWithCredential(auth, nativeCredential);
|
330 |
|
331 |
-
return this.createSignInResult(webAuthResult
|
332 |
} else {
|
333 |
return Promise.reject('null nativeAuthResult');
|
334 |
}
|
@@ -336,34 +314,42 @@ export class FirebaseAuthService implements OnDestroy {
|
|
336 |
|
337 |
public async signInWithFacebook(): Promise<SignInResult> {
|
338 |
const provider = new FacebookAuthProvider();
|
339 |
-
const
|
|
|
|
|
340 |
|
341 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
342 |
-
return this.socialSignIn(provider,
|
343 |
}
|
344 |
|
345 |
public async signInWithGoogle(): Promise<SignInResult> {
|
346 |
const provider = new GoogleAuthProvider();
|
347 |
-
const
|
|
|
|
|
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,
|
351 |
}
|
352 |
|
353 |
public async signInWithTwitter(): Promise<SignInResult> {
|
354 |
const provider = new TwitterAuthProvider();
|
355 |
-
const
|
|
|
|
|
356 |
|
357 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
358 |
-
return this.socialSignIn(provider,
|
359 |
}
|
360 |
|
361 |
public async signInWithApple(): Promise<SignInResult> {
|
362 |
const provider = new OAuthProvider('apple.com');
|
363 |
-
const
|
|
|
|
|
364 |
|
365 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
366 |
-
return this.socialSignIn(provider,
|
367 |
}
|
368 |
|
369 |
public async signInWithEmail(email: string, password: string): Promise<SignInResult> {
|
@@ -375,7 +361,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
375 |
|
376 |
this.dismissLoading();
|
377 |
|
378 |
-
return this.
|
379 |
}
|
380 |
|
381 |
public async signUpWithEmail(email: string, password: string): Promise<SignInResult> {
|
@@ -387,7 +373,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
387 |
|
388 |
this.dismissLoading();
|
389 |
|
390 |
-
return this.
|
391 |
}
|
392 |
|
393 |
public get redirectResult$(): Observable<any> {
|
@@ -404,7 +390,7 @@ export class FirebaseAuthService implements OnDestroy {
|
|
404 |
.pipe(
|
405 |
filter((user: FirebaseUser) => user != null),
|
406 |
map((user: FirebaseUser) => {
|
407 |
-
const userResult = this.createUserResult(user);
|
408 |
return this.setUserModelForProfile(userResult);
|
409 |
})
|
410 |
);
|
@@ -450,59 +436,4 @@ export class FirebaseAuthService implements OnDestroy {
|
|
450 |
return photoURL;
|
451 |
}
|
452 |
}
|
453 |
-
|
454 |
-
// * Aux methods inspired on the @capacitor-firebase/authentication library
|
455 |
-
|
456 |
-
// (see: https://github.com/robingenz/capacitor-firebase/blob/a51927ff3acce94cedcd7bfc218952bb106db904/packages/authentication/src/web.ts#L297)
|
457 |
-
private createSignInResultFromUserCredential(credential: UserCredential): SignInResult {
|
458 |
-
const userResult = this.createUserResult(credential.user);
|
459 |
-
const result: SignInResult = {
|
460 |
-
user: userResult,
|
461 |
-
credential: null,
|
462 |
-
};
|
463 |
-
return result;
|
464 |
-
}
|
465 |
-
|
466 |
-
private createSignInResult(user: FirebaseUser, credential: FirebaseAuthCredential | null): SignInResult {
|
467 |
-
const userResult = this.createUserResult(user);
|
468 |
-
const credentialResult = this.createCredentialResult(credential);
|
469 |
-
const result: SignInResult = {
|
470 |
-
user: userResult,
|
471 |
-
credential: credentialResult,
|
472 |
-
};
|
473 |
-
return result;
|
474 |
-
}
|
475 |
-
|
476 |
-
private createUserResult(user: FirebaseUser | null): User | null {
|
477 |
-
if (!user) {
|
478 |
-
return null;
|
479 |
-
}
|
480 |
-
const result: User = {
|
481 |
-
displayName: user.displayName,
|
482 |
-
email: user.email,
|
483 |
-
emailVerified: user.emailVerified,
|
484 |
-
isAnonymous: user.isAnonymous,
|
485 |
-
phoneNumber: user.phoneNumber,
|
486 |
-
photoUrl: user.photoURL,
|
487 |
-
providerId: user.providerId,
|
488 |
-
tenantId: user.tenantId,
|
489 |
-
uid: user.uid,
|
490 |
-
};
|
491 |
-
return result;
|
492 |
-
}
|
493 |
-
|
494 |
-
private createCredentialResult(credential: FirebaseAuthCredential | null): AuthCredential | null {
|
495 |
-
if (!credential) {
|
496 |
-
return null;
|
497 |
-
}
|
498 |
-
const result: AuthCredential = {
|
499 |
-
providerId: credential.providerId,
|
500 |
-
};
|
501 |
-
if (credential instanceof OAuthCredential) {
|
502 |
-
result.accessToken = credential.accessToken;
|
503 |
-
result.idToken = credential.idToken;
|
504 |
-
result.secret = credential.secret;
|
505 |
-
}
|
506 |
-
return result;
|
507 |
-
}
|
508 |
}
|
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 { DataStore } from '../../shell/data-store';
|
18 |
import { FirebaseProfileModel } from './profile/firebase-profile.model';
|
19 |
import { SignInProvider } from './firebase-auth-definitions';
|
20 |
+
import { FirebaseAuthHelper } from './firebase-auth.helper';
|
21 |
|
22 |
|
23 |
({
|
35 |
public route: ActivatedRoute,
|
36 |
public platform: Platform,
|
37 |
private ngZone: NgZone,
|
38 |
+
private firebaseAuthHelper: FirebaseAuthHelper,
|
39 |
public loadingController: LoadingController,
|
40 |
public location: Location,
|
41 |
PLATFORM_ID) private platformId: object
(
|
91 |
break;
|
92 |
}
|
93 |
|
94 |
+
const signInResult = firebaseAuthHelper.createSignInResult(result, credential);
|
95 |
|
96 |
this.dismissLoading();
|
97 |
|
133 |
this.dismissLoading();
|
134 |
}
|
135 |
|
136 |
+
private prepareForAuthWithProvidersRedirection(authProviderId: string): void {
|
137 |
+
// ? Before invoking auth provider redirect flow, add a flag to the path.
|
138 |
+
// ? The presence of the flag in the path indicates we should wait for the auth redirect to complete
|
139 |
+
this.location.replaceState(this.location.path(), 'auth-redirect=' + authProviderId, this.location.getState());
|
140 |
+
}
|
141 |
+
|
142 |
+
private clearAuthWithProvidersRedirection(): void {
|
143 |
+
// ? Remove auth-redirect param from url
|
144 |
+
this.location.replaceState(this.router.url.split('?')[0], '');
|
145 |
+
this.dismissLoading();
|
146 |
+
}
|
147 |
+
|
148 |
+
private async presentLoading(authProviderId?: string): Promise<void> {
|
149 |
+
const authProviderCapitalized = authProviderId[0].toUpperCase() + authProviderId.slice(1);
|
150 |
+
|
151 |
+
this.loadingController.create({
|
152 |
+
message: authProviderId ? 'Signing in with ' + authProviderCapitalized : 'Signing in ...',
|
153 |
+
duration: 4000
|
154 |
+
}).then((loader) => {
|
155 |
+
this.authLoader = loader;
|
156 |
+
this.authLoader.present();
|
157 |
+
});
|
158 |
+
}
|
159 |
+
|
160 |
+
private async dismissLoading(): Promise<void> {
|
161 |
+
if (this.authLoader) {
|
162 |
+
await this.authLoader.dismiss();
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
public async signOut(): Promise<string> {
|
167 |
const signOutPromise = new Promise<string>((resolve, reject) => {
|
168 |
// * 1. Sign out on the native layer
|
187 |
return signOutPromise;
|
188 |
}
|
189 |
|
190 |
+
private async socialSignIn(provider: (OAuthProvider | GoogleAuthProvider | FacebookAuthProvider | TwitterAuthProvider), authOptions?: SignInWithOAuthOptions): Promise<SignInResult> {
|
191 |
this.presentLoading(provider.providerId);
|
192 |
|
193 |
let authResult: SignInResult = null;
|
194 |
|
195 |
if (this.platform.is('capacitor')) {
|
196 |
+
authResult = await this.nativeAuth(provider, authOptions);
|
197 |
} else {
|
198 |
+
authResult = await this.webAuth(provider, authOptions);
|
199 |
}
|
200 |
|
201 |
this.dismissLoading();
|
207 |
}
|
208 |
}
|
209 |
|
210 |
+
private async webAuth(provider: (OAuthProvider | GoogleAuthProvider | FacebookAuthProvider | TwitterAuthProvider), authOptions?: SignInWithOAuthOptions): Promise<SignInResult> {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
const auth = getAuth();
|
212 |
let webAuthUserCredential: UserCredential = null;
|
213 |
|
214 |
+
this.firebaseAuthHelper.applySignInOptions(authOptions || {}, provider);
|
215 |
+
|
216 |
if (this.platform.is('desktop')) {
|
217 |
webAuthUserCredential = await signInWithPopup(auth, provider);
|
218 |
} else {
|
242 |
break;
|
243 |
}
|
244 |
|
245 |
+
return this.firebaseAuthHelper.createSignInResult(webAuthUserCredential, webCredential);
|
246 |
} else {
|
247 |
return Promise.reject('null webAuthUserCredential');
|
248 |
}
|
249 |
}
|
250 |
|
251 |
+
private async nativeAuth(provider: AuthProvider, authOptions?: SignInWithOAuthOptions): Promise<SignInResult> {
|
252 |
let nativeAuthResult: SignInResult = null;
|
253 |
|
|
|
|
|
|
|
|
|
|
|
254 |
// * 1. Sign in on the native layer
|
255 |
switch (provider.providerId) {
|
256 |
case SignInProvider.apple:
|
257 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithApple(authOptions);
|
258 |
break;
|
259 |
case SignInProvider.facebook:
|
260 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithFacebook(authOptions);
|
261 |
break;
|
262 |
case SignInProvider.google:
|
263 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithGoogle(authOptions);
|
264 |
break;
|
265 |
case SignInProvider.twitter:
|
266 |
+
nativeAuthResult = await FirebaseAuthentication.signInWithTwitter(authOptions);
|
267 |
break;
|
268 |
}
|
269 |
|
286 |
);
|
287 |
break;
|
288 |
case SignInProvider.google:
|
289 |
+
nativeCredential = GoogleAuthProvider.credential(
|
290 |
+
nativeAuthResult.credential?.idToken,
|
291 |
+
nativeAuthResult.credential?.accessToken
|
292 |
+
);
|
293 |
break;
|
294 |
case SignInProvider.twitter:
|
295 |
try {
|
296 |
+
nativeCredential = TwitterAuthProvider.credential(
|
297 |
+
nativeAuthResult.credential?.accessToken,
|
298 |
+
nativeAuthResult.credential?.secret
|
299 |
+
);
|
300 |
break;
|
301 |
} catch (error) {
|
302 |
console.error(error);
|
306 |
// * 2. Sign in on the web layer using the access token we got from the native sign in
|
307 |
const webAuthResult = await signInWithCredential(auth, nativeCredential);
|
308 |
|
309 |
+
return this.firebaseAuthHelper.createSignInResult(webAuthResult, nativeCredential);
|
310 |
} else {
|
311 |
return Promise.reject('null nativeAuthResult');
|
312 |
}
|
314 |
|
315 |
public async signInWithFacebook(): Promise<SignInResult> {
|
316 |
const provider = new FacebookAuthProvider();
|
317 |
+
const authOptions: SignInWithOAuthOptions = {
|
318 |
+
scopes: ['email', 'public_profile']
|
319 |
+
};
|
320 |
|
321 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
322 |
+
return this.socialSignIn(provider, authOptions);
|
323 |
}
|
324 |
|
325 |
public async signInWithGoogle(): Promise<SignInResult> {
|
326 |
const provider = new GoogleAuthProvider();
|
327 |
+
const authOptions: SignInWithOAuthOptions = {
|
328 |
+
scopes: ['email', 'profile']
|
329 |
+
};
|
330 |
|
331 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
332 |
+
return this.socialSignIn(provider, authOptions);
|
333 |
}
|
334 |
|
335 |
public async signInWithTwitter(): Promise<SignInResult> {
|
336 |
const provider = new TwitterAuthProvider();
|
337 |
+
const authOptions: SignInWithOAuthOptions = {
|
338 |
+
scopes: ['email', 'name']
|
339 |
+
};
|
340 |
|
341 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
342 |
+
return this.socialSignIn(provider, authOptions);
|
343 |
}
|
344 |
|
345 |
public async signInWithApple(): Promise<SignInResult> {
|
346 |
const provider = new OAuthProvider('apple.com');
|
347 |
+
const authOptions: SignInWithOAuthOptions = {
|
348 |
+
scopes: ['email', 'name']
|
349 |
+
};
|
350 |
|
351 |
// ? When we use the redirect authentication flow, the code below the socialSignIn() invocation does not get executed as we leave the current page
|
352 |
+
return this.socialSignIn(provider, authOptions);
|
353 |
}
|
354 |
|
355 |
public async signInWithEmail(email: string, password: string): Promise<SignInResult> {
|
361 |
|
362 |
this.dismissLoading();
|
363 |
|
364 |
+
return this.firebaseAuthHelper.createSignInResult(credential, null);
|
365 |
}
|
366 |
|
367 |
public async signUpWithEmail(email: string, password: string): Promise<SignInResult> {
|
373 |
|
374 |
this.dismissLoading();
|
375 |
|
376 |
+
return this.firebaseAuthHelper.createSignInResult(credential, null);
|
377 |
}
|
378 |
|
379 |
public get redirectResult$(): Observable<any> {
|
390 |
.pipe(
|
391 |
filter((user: FirebaseUser) => user != null),
|
392 |
map((user: FirebaseUser) => {
|
393 |
+
const userResult = this.firebaseAuthHelper.createUserResult(user);
|
394 |
return this.setUserModelForProfile(userResult);
|
395 |
})
|
396 |
);
|
436 |
return photoURL;
|
437 |
}
|
438 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
439 |
}
|
@@ -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 |
|
@@ -16,7 +16,7 @@ import { FirebaseAuthService } from '../firebase-auth.service';
|
|
16 |
]
|
17 |
})
|
18 |
export class FirebaseSignInPage {
|
19 |
-
loginForm:
|
20 |
submitError: string;
|
21 |
authRedirectResult: Subscription;
|
22 |
|
@@ -37,12 +37,12 @@ export class FirebaseSignInPage {
|
|
37 |
private ngZone: NgZone,
|
38 |
public historyHelper: HistoryHelperService
|
39 |
) {
|
40 |
-
this.loginForm = new
|
41 |
-
'email': new
|
42 |
Validators.required,
|
43 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
44 |
])),
|
45 |
-
'password': new
|
46 |
Validators.minLength(6),
|
47 |
Validators.required
|
48 |
]))
|
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 |
|
16 |
]
|
17 |
})
|
18 |
export class FirebaseSignInPage {
|
19 |
+
loginForm: UntypedFormGroup;
|
20 |
submitError: string;
|
21 |
authRedirectResult: Subscription;
|
22 |
|
37 |
private ngZone: NgZone,
|
38 |
public historyHelper: HistoryHelperService
|
39 |
) {
|
40 |
+
this.loginForm = new UntypedFormGroup({
|
41 |
+
'email': new UntypedFormControl('', Validators.compose([
|
42 |
Validators.required,
|
43 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
44 |
])),
|
45 |
+
'password': new UntypedFormControl('', Validators.compose([
|
46 |
Validators.minLength(6),
|
47 |
Validators.required
|
48 |
]))
|
@@ -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 |
|
@@ -19,8 +19,8 @@ import { FirebaseAuthService } from '../firebase-auth.service';
|
|
19 |
]
|
20 |
})
|
21 |
export class FirebaseSignUpPage implements OnInit {
|
22 |
-
signupForm:
|
23 |
-
matching_passwords_group:
|
24 |
submitError: string;
|
25 |
authRedirectResult: Subscription;
|
26 |
|
@@ -48,18 +48,18 @@ export class FirebaseSignUpPage implements OnInit {
|
|
48 |
private ngZone: NgZone,
|
49 |
public historyHelper: HistoryHelperService
|
50 |
) {
|
51 |
-
this.matching_passwords_group = new
|
52 |
-
'password': new
|
53 |
Validators.minLength(6),
|
54 |
Validators.required
|
55 |
])),
|
56 |
-
'confirm_password': new
|
57 |
-
}, (formGroup:
|
58 |
return PasswordValidator.areNotEqual(formGroup);
|
59 |
});
|
60 |
|
61 |
-
this.signupForm = new
|
62 |
-
'email': new
|
63 |
Validators.required,
|
64 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
65 |
])),
|
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 |
|
19 |
]
|
20 |
})
|
21 |
export class FirebaseSignUpPage implements OnInit {
|
22 |
+
signupForm: UntypedFormGroup;
|
23 |
+
matching_passwords_group: UntypedFormGroup;
|
24 |
submitError: string;
|
25 |
authRedirectResult: Subscription;
|
26 |
|
48 |
private ngZone: NgZone,
|
49 |
public historyHelper: HistoryHelperService
|
50 |
) {
|
51 |
+
this.matching_passwords_group = new UntypedFormGroup({
|
52 |
+
'password': new UntypedFormControl('', Validators.compose([
|
53 |
Validators.minLength(6),
|
54 |
Validators.required
|
55 |
])),
|
56 |
+
'confirm_password': new UntypedFormControl('', Validators.required)
|
57 |
+
}, (formGroup: UntypedFormGroup) => {
|
58 |
return PasswordValidator.areNotEqual(formGroup);
|
59 |
});
|
60 |
|
61 |
+
this.signupForm = new UntypedFormGroup({
|
62 |
+
'email': new UntypedFormControl('', Validators.compose([
|
63 |
Validators.required,
|
64 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
65 |
])),
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, OnInit, OnDestroy, HostBinding } from '@angular/core';
|
2 |
-
import {
|
3 |
import { ModalController, IonRouterOutlet } from '@ionic/angular';
|
4 |
import { ActivatedRoute } from '@angular/router';
|
5 |
|
@@ -22,7 +22,7 @@ import { FirebaseCrudService } from '../firebase-crud.service';
|
|
22 |
],
|
23 |
})
|
24 |
export class FirebaseListingPage implements OnInit, OnDestroy {
|
25 |
-
rangeForm:
|
26 |
searchQuery: string;
|
27 |
showAgeFilter = false;
|
28 |
|
@@ -54,8 +54,8 @@ export class FirebaseListingPage implements OnInit, OnDestroy {
|
|
54 |
ngOnInit() {
|
55 |
this.searchQuery = '';
|
56 |
|
57 |
-
this.rangeForm = new
|
58 |
-
dual: new
|
59 |
});
|
60 |
|
61 |
this.route.data
|
1 |
import { Component, OnInit, OnDestroy, HostBinding } from '@angular/core';
|
2 |
+
import { UntypedFormGroup, UntypedFormControl } from '@angular/forms';
|
3 |
import { ModalController, IonRouterOutlet } from '@ionic/angular';
|
4 |
import { ActivatedRoute } from '@angular/router';
|
5 |
|
22 |
],
|
23 |
})
|
24 |
export class FirebaseListingPage implements OnInit, OnDestroy {
|
25 |
+
rangeForm: UntypedFormGroup;
|
26 |
searchQuery: string;
|
27 |
showAgeFilter = false;
|
28 |
|
54 |
ngOnInit() {
|
55 |
this.searchQuery = '';
|
56 |
|
57 |
+
this.rangeForm = new UntypedFormGroup({
|
58 |
+
dual: new UntypedFormControl({lower: 1, upper: 100})
|
59 |
});
|
60 |
|
61 |
this.route.data
|
@@ -1,6 +1,6 @@
|
|
1 |
import { Component, Input, OnInit } from '@angular/core';
|
2 |
import { ModalController } from '@ionic/angular';
|
3 |
-
import { Validators,
|
4 |
import * as dayjs from 'dayjs';
|
5 |
|
6 |
import { CheckboxCheckedValidator } from '../../../../validators/checkbox-checked.validator';
|
@@ -23,7 +23,7 @@ export class FirebaseCreateUserModalComponent implements OnInit {
|
|
23 |
// We use it to for the dismiss function
|
24 |
modalId: string;
()
|
25 |
|
26 |
-
createUserForm:
|
27 |
userData: FirebaseUserModel = new FirebaseUserModel();
|
28 |
skills = [];
|
29 |
formattedDate?: string;
|
@@ -37,26 +37,26 @@ export class FirebaseCreateUserModalComponent implements OnInit {
|
|
37 |
// default image
|
38 |
this.userData.avatar = 'https://s3-us-west-2.amazonaws.com/ionicthemes/otros/avatar-placeholder.png';
|
39 |
|
40 |
-
this.createUserForm = new
|
41 |
-
name: new
|
42 |
-
lastname: new
|
43 |
-
email: new
|
44 |
Validators.required,
|
45 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
46 |
])),
|
47 |
-
phone: new
|
48 |
-
birthdate: new
|
49 |
-
skills: new
|
50 |
-
spanish: new
|
51 |
-
english: new
|
52 |
-
french: new
|
53 |
});
|
54 |
|
55 |
this.firebaseCrudService.getSkills().subscribe(skills => {
|
56 |
this.skills = skills;
|
57 |
// create skill checkboxes
|
58 |
this.skills.map(() => {
|
59 |
-
(this.createUserForm.controls.skills as
|
60 |
});
|
61 |
});
|
62 |
}
|
@@ -65,7 +65,7 @@ export class FirebaseCreateUserModalComponent implements OnInit {
|
|
65 |
this.formattedDate = format(parseISO(this.createUserForm.value.birthdate), 'MMM d, yyyy');
|
66 |
}
|
67 |
|
68 |
-
get skillsFormArray() { return <
|
69 |
|
70 |
changeLangValue(value): string {
|
71 |
switch (true) {
|
1 |
import { Component, Input, OnInit } from '@angular/core';
|
2 |
import { ModalController } from '@ionic/angular';
|
3 |
+
import { Validators, UntypedFormGroup, UntypedFormControl, UntypedFormArray } from '@angular/forms';
|
4 |
import * as dayjs from 'dayjs';
|
5 |
|
6 |
import { CheckboxCheckedValidator } from '../../../../validators/checkbox-checked.validator';
|
23 |
// We use it to for the dismiss function
|
24 |
modalId: string;
()
|
25 |
|
26 |
+
createUserForm: UntypedFormGroup;
|
27 |
userData: FirebaseUserModel = new FirebaseUserModel();
|
28 |
skills = [];
|
29 |
formattedDate?: string;
|
37 |
// default image
|
38 |
this.userData.avatar = 'https://s3-us-west-2.amazonaws.com/ionicthemes/otros/avatar-placeholder.png';
|
39 |
|
40 |
+
this.createUserForm = new UntypedFormGroup({
|
41 |
+
name: new UntypedFormControl('', Validators.required),
|
42 |
+
lastname: new UntypedFormControl('', Validators.required),
|
43 |
+
email: new UntypedFormControl('', Validators.compose([
|
44 |
Validators.required,
|
45 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
46 |
])),
|
47 |
+
phone: new UntypedFormControl('', Validators.required),
|
48 |
+
birthdate: new UntypedFormControl(null, Validators.required),
|
49 |
+
skills: new UntypedFormArray([], CheckboxCheckedValidator.minSelectedCheckboxes(1)),
|
50 |
+
spanish: new UntypedFormControl(),
|
51 |
+
english: new UntypedFormControl(),
|
52 |
+
french: new UntypedFormControl()
|
53 |
});
|
54 |
|
55 |
this.firebaseCrudService.getSkills().subscribe(skills => {
|
56 |
this.skills = skills;
|
57 |
// create skill checkboxes
|
58 |
this.skills.map(() => {
|
59 |
+
(this.createUserForm.controls.skills as UntypedFormArray).push(new UntypedFormControl());
|
60 |
});
|
61 |
});
|
62 |
}
|
65 |
this.formattedDate = format(parseISO(this.createUserForm.value.birthdate), 'MMM d, yyyy');
|
66 |
}
|
67 |
|
68 |
+
get skillsFormArray() { return <UntypedFormArray>this.createUserForm.get('skills'); }
|
69 |
|
70 |
changeLangValue(value): string {
|
71 |
switch (true) {
|
@@ -1,6 +1,6 @@
|
|
1 |
import { Component, OnInit, Input, NgZone } from '@angular/core';
|
2 |
import { ModalController, AlertController } from '@ionic/angular';
|
3 |
-
import { Validators,
|
4 |
import { Router } from '@angular/router';
|
5 |
|
6 |
import * as dayjs from 'dayjs';
|
@@ -28,7 +28,7 @@ export class FirebaseUpdateUserModalComponent implements OnInit {
|
|
28 |
// We use it to for the dismiss function
|
29 |
modalId: string;
()
|
30 |
|
31 |
-
updateUserForm:
|
32 |
selectedAvatar: string;
|
33 |
skills = [];
|
34 |
formattedDate?: string;
|
@@ -44,19 +44,19 @@ export class FirebaseUpdateUserModalComponent implements OnInit {
|
|
44 |
ngOnInit() {
|
45 |
this.selectedAvatar = this.user.avatar;
|
46 |
|
47 |
-
this.updateUserForm = new
|
48 |
-
name: new
|
49 |
-
lastname: new
|
50 |
-
email: new
|
51 |
Validators.required,
|
52 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
53 |
])),
|
54 |
-
phone: new
|
55 |
-
birthdate: new
|
56 |
-
skills: new
|
57 |
-
spanish: new
|
58 |
-
english: new
|
59 |
-
french: new
|
60 |
});
|
61 |
|
62 |
this.formatDate();
|
@@ -72,8 +72,8 @@ export class FirebaseUpdateUserModalComponent implements OnInit {
|
|
72 |
});
|
73 |
}
|
74 |
// set the control value to 'true' if the user already has this skill
|
75 |
-
const control = new
|
76 |
-
(this.updateUserForm.controls.skills as
|
77 |
});
|
78 |
});
|
79 |
}
|
@@ -82,7 +82,7 @@ export class FirebaseUpdateUserModalComponent implements OnInit {
|
|
82 |
this.formattedDate = format(parseISO(this.updateUserForm.value.birthdate), 'MMM d, yyyy');
|
83 |
}
|
84 |
|
85 |
-
get skillsFormArray() { return <
|
86 |
|
87 |
changeLangValue(value): string {
|
88 |
switch (true) {
|
1 |
import { Component, OnInit, Input, NgZone } from '@angular/core';
|
2 |
import { ModalController, AlertController } from '@ionic/angular';
|
3 |
+
import { Validators, UntypedFormGroup, UntypedFormControl, UntypedFormArray } from '@angular/forms';
|
4 |
import { Router } from '@angular/router';
|
5 |
|
6 |
import * as dayjs from 'dayjs';
|
28 |
// We use it to for the dismiss function
|
29 |
modalId: string;
()
|
30 |
|
31 |
+
updateUserForm: UntypedFormGroup;
|
32 |
selectedAvatar: string;
|
33 |
skills = [];
|
34 |
formattedDate?: string;
|
44 |
ngOnInit() {
|
45 |
this.selectedAvatar = this.user.avatar;
|
46 |
|
47 |
+
this.updateUserForm = new UntypedFormGroup({
|
48 |
+
name: new UntypedFormControl(this.user.name, Validators.required),
|
49 |
+
lastname: new UntypedFormControl(this.user.lastname, Validators.required),
|
50 |
+
email: new UntypedFormControl(this.user.email, Validators.compose([
|
51 |
Validators.required,
|
52 |
Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
|
53 |
])),
|
54 |
+
phone: new UntypedFormControl(this.user.phone, Validators.required),
|
55 |
+
birthdate: new UntypedFormControl(dayjs.unix(this.user.birthdate).format(), Validators.required),
|
56 |
+
skills: new UntypedFormArray([], CheckboxCheckedValidator.minSelectedCheckboxes(1)),
|
57 |
+
spanish: new UntypedFormControl(this.user.languages.spanish),
|
58 |
+
english: new UntypedFormControl(this.user.languages.english),
|
59 |
+
french: new UntypedFormControl(this.user.languages.french)
|
60 |
});
|
61 |
|
62 |
this.formatDate();
|
72 |
});
|
73 |
}
|
74 |
// set the control value to 'true' if the user already has this skill
|
75 |
+
const control = new UntypedFormControl(userSkillsIds.includes(skill.id));
|
76 |
+
(this.updateUserForm.controls.skills as UntypedFormArray).push(control);
|
77 |
});
|
78 |
});
|
79 |
}
|
82 |
this.formattedDate = format(parseISO(this.updateUserForm.value.birthdate), 'MMM d, yyyy');
|
83 |
}
|
84 |
|
85 |
+
get skillsFormArray() { return <UntypedFormArray>this.updateUserForm.get('skills'); }
|
86 |
|
87 |
changeLangValue(value): string {
|
88 |
switch (true) {
|
@@ -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 |
'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 |
'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 |
import { CustomComponentsPage } from './custom-components.page';
|
@@ -13,7 +12,6 @@ import { ShowcaseService } from '../showcase.service';
|
|
13 |
IonicModule,
|
14 |
CommonModule,
|
15 |
ComponentsModule,
|
16 |
-
FormsModule,
|
17 |
RouterModule.forChild([
|
18 |
{
|
19 |
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 { CustomComponentsPage } from './custom-components.page';
|
12 |
IonicModule,
|
13 |
CommonModule,
|
14 |
ComponentsModule,
|
|
|
15 |
RouterModule.forChild([
|
16 |
{
|
17 |
path: '',
|
@@ -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 = [
|
@@ -35,7 +34,6 @@ const showcaseRoutes: Routes = [
|
|
35 |
imports: [
|
36 |
IonicModule,
|
37 |
CommonModule,
|
38 |
-
FormsModule,
|
39 |
RouterModule.forChild(showcaseRoutes),
|
40 |
ComponentsModule
|
41 |
],
|
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 = [
|
34 |
imports: [
|
35 |
IonicModule,
|
36 |
CommonModule,
|
|
|
37 |
RouterModule.forChild(showcaseRoutes),
|
38 |
ComponentsModule
|
39 |
],
|
@@ -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,9 +1,9 @@
|
|
1 |
-
import { ValidatorFn,
|
2 |
|
3 |
export class CheckboxCheckedValidator {
|
4 |
|
5 |
static minSelectedCheckboxes(min) {
|
6 |
-
const validator: ValidatorFn = (formArray:
|
7 |
const totalSelected = formArray.controls
|
8 |
// get a list of checkbox values (boolean)
|
9 |
.map(control => control.value)
|
1 |
+
import { ValidatorFn, UntypedFormArray } from '@angular/forms';
|
2 |
|
3 |
export class CheckboxCheckedValidator {
|
4 |
|
5 |
static minSelectedCheckboxes(min) {
|
6 |
+
const validator: ValidatorFn = (formArray: UntypedFormArray) => {
|
7 |
const totalSelected = formArray.controls
|
8 |
// get a list of checkbox values (boolean)
|
9 |
.map(control => control.value)
|
@@ -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.
|
@@ -6,12 +6,14 @@ import { NgxChartsModule } from "@swimlane/ngx-charts";
|
|
6 |
|
7 |
import { CustomDoughnutChartComponent } from './custom-doughnut-chart/custom-doughnut-chart.component';
|
8 |
import { CustomLineChartComponent } from './custom-line-chart/custom-line-chart.component';
|
|
|
9 |
|
10 |
|
11 |
({
|
12 |
declarations: [
|
13 |
CustomDoughnutChartComponent,
|
14 |
-
CustomLineChartComponent
|
|
|
15 |
],
|
16 |
imports: [
|
17 |
CommonModule,
|
@@ -19,7 +21,8 @@ import { CustomLineChartComponent } from './custom-line-chart/custom-line-chart.
|
|
19 |
],
|
20 |
exports: [
|
21 |
CustomDoughnutChartComponent,
|
22 |
-
CustomLineChartComponent
|
|
|
23 |
]
|
24 |
})
|
25 |
export class ChartsSharedModule { }
|
6 |
|
7 |
import { CustomDoughnutChartComponent } from './custom-doughnut-chart/custom-doughnut-chart.component';
|
8 |
import { CustomLineChartComponent } from './custom-line-chart/custom-line-chart.component';
|
9 |
+
import { CustomProgressBarDirective } from './custom-progress-bar-chart/custom-progress-bar.directive';
|
10 |
|
11 |
|
12 |
({
|
13 |
declarations: [
|
14 |
CustomDoughnutChartComponent,
|
15 |
+
CustomLineChartComponent,
|
16 |
+
CustomProgressBarDirective
|
17 |
],
|
18 |
imports: [
|
19 |
CommonModule,
|
21 |
],
|
22 |
exports: [
|
23 |
CustomDoughnutChartComponent,
|
24 |
+
CustomLineChartComponent,
|
25 |
+
CustomProgressBarDirective
|
26 |
]
|
27 |
})
|
28 |
export class ChartsSharedModule { }
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Directive, Input, OnInit } from '@angular/core';
|
2 |
+
import { IonProgressBar } from '@ionic/angular';
|
3 |
+
|
4 |
+
({
|
5 |
+
selector: '[appCustomProgressBar]'
|
6 |
+
})
|
7 |
+
export class CustomProgressBarDirective implements OnInit {
|
8 |
+
|
9 |
+
ionicProgressBar: IonProgressBar;
|
10 |
+
|
11 |
+
// ? Make Inputs required
|
12 |
+
// (see: https://stackoverflow.com/a/50293330/1116959)
|
13 |
+
()
|
14 |
+
get progress() {
|
15 |
+
throw new Error('Attribute "appCustomProgressBar.progress" is required');
|
16 |
+
}
|
17 |
+
set progress(value: number) {
|
18 |
+
Object.defineProperty(this, 'progress', {
|
19 |
+
value,
|
20 |
+
writable: true,
|
21 |
+
configurable: true
|
22 |
+
});
|
23 |
+
}
|
24 |
+
|
25 |
+
()
|
26 |
+
get max() {
|
27 |
+
throw new Error('Attribute "appCustomProgressBar.max" is required');
|
28 |
+
}
|
29 |
+
set max(value: number) {
|
30 |
+
Object.defineProperty(this, 'max', {
|
31 |
+
value,
|
32 |
+
writable: true,
|
33 |
+
configurable: true
|
34 |
+
});
|
35 |
+
}
|
36 |
+
|
37 |
+
constructor(private hostElement: IonProgressBar) {
|
38 |
+
this.ionicProgressBar = hostElement;
|
39 |
+
}
|
40 |
+
|
41 |
+
ngOnInit(): void {
|
42 |
+
this.ionicProgressBar.buffer = 1;
|
43 |
+
|
44 |
+
const progressValue = (this.progress * 100) / this.max;
|
45 |
+
|
46 |
+
this.ionicProgressBar.value = (progressValue / 100);
|
47 |
+
}
|
48 |
+
}
|
@@ -4,8 +4,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
4 |
import { RouterModule, Routes } from "@angular/router";
|
5 |
import { IonicModule } from "@ionic/angular";
|
6 |
|
7 |
-
// https://ng-bootstrap.github.io/#/components/progressbar/api
|
8 |
-
import { NgbProgressbarModule } from "@ng-bootstrap/ng-bootstrap";
|
9 |
// ngx-charts - lib: https://swimlane.gitbook.io/ngx-charts/
|
10 |
import { NgxChartsModule } from "@swimlane/ngx-charts";
|
11 |
|
@@ -29,8 +27,7 @@ const routes: Routes = [
|
|
29 |
IonicModule,
|
30 |
RouterModule.forChild(routes),
|
31 |
ChartsSharedModule,
|
32 |
-
NgxChartsModule
|
33 |
-
NgbProgressbarModule
|
34 |
],
|
35 |
declarations: [
|
36 |
CrmDashboardPage
|
4 |
import { RouterModule, Routes } from "@angular/router";
|
5 |
import { IonicModule } from "@ionic/angular";
|
6 |
|
|
|
|
|
7 |
// ngx-charts - lib: https://swimlane.gitbook.io/ngx-charts/
|
8 |
import { NgxChartsModule } from "@swimlane/ngx-charts";
|
9 |
|
27 |
IonicModule,
|
28 |
RouterModule.forChild(routes),
|
29 |
ChartsSharedModule,
|
30 |
+
NgxChartsModule
|
|
|
31 |
],
|
32 |
declarations: [
|
33 |
CrmDashboardPage
|
@@ -46,14 +46,14 @@
|
|
46 |
<div class="progress-bar-stat-wrapper">
|
47 |
<div class="progress-bar-stat">
|
48 |
<div class="progress-bar-container">
|
49 |
-
<
|
50 |
</div>
|
51 |
<span class="stat-value" ngx-charts-count-up [countTo]="79">79</span>
|
52 |
<span class="stat-reference">new</span>
|
53 |
</div>
|
54 |
<div class="progress-bar-stat">
|
55 |
<div class="progress-bar-container">
|
56 |
-
<
|
57 |
</div>
|
58 |
<span class="stat-value" ngx-charts-count-up [countTo]="18" [countSuffix]="'%'">18%</span>
|
59 |
<span class="stat-reference">Subscribed</span>
|
46 |
<div class="progress-bar-stat-wrapper">
|
47 |
<div class="progress-bar-stat">
|
48 |
<div class="progress-bar-container">
|
49 |
+
<ion-progress-bar appCustomProgressBar [progress]="79" [max]="254"></ion-progress-bar>
|
50 |
</div>
|
51 |
<span class="stat-value" ngx-charts-count-up [countTo]="79">79</span>
|
52 |
<span class="stat-reference">new</span>
|
53 |
</div>
|
54 |
<div class="progress-bar-stat">
|
55 |
<div class="progress-bar-container">
|
56 |
+
<ion-progress-bar appCustomProgressBar [progress]="18" [max]="100"></ion-progress-bar>
|
57 |
</div>
|
58 |
<span class="stat-value" ngx-charts-count-up [countTo]="18" [countSuffix]="'%'">18%</span>
|
59 |
<span class="stat-reference">Subscribed</span>
|
@@ -71,6 +71,7 @@
|
|
71 |
|
72 |
.progress-bar-stat-wrapper {
|
73 |
--progress-bar-height: 20vw;
|
|
|
74 |
|
75 |
display: flex;
|
76 |
justify-content: center;
|
@@ -107,21 +108,25 @@
|
|
107 |
justify-content: center;
|
108 |
align-items: center;
|
109 |
|
110 |
-
|
|
|
111 |
// ? As it is rotated, height references the actual width of the object
|
112 |
-
--
|
113 |
// ? Half the height
|
114 |
-
|
115 |
-
--bs-progress-bar-bg: #2c93e8;
|
116 |
-
--bs-progress-bg: #d5e7f8;
|
117 |
-
|
118 |
-
transform: rotate(-90deg);
|
119 |
// ? As it is rotated, width references the actual height of the object
|
120 |
width: var(--progress-bar-height);
|
121 |
-
|
|
|
|
|
|
|
|
|
122 |
// ? Inner bar
|
123 |
-
|
124 |
-
border
|
|
|
|
|
|
|
125 |
}
|
126 |
}
|
127 |
}
|
@@ -189,7 +194,7 @@
|
|
189 |
}
|
190 |
|
191 |
.tag-label {
|
192 |
-
font-size:
|
193 |
text-overflow: unset;
|
194 |
white-space: unset;
|
195 |
overflow: initial;
|
@@ -201,7 +206,7 @@
|
|
201 |
padding: 20px 0px;
|
202 |
border-radius: 35px 35px 0px 0px;
|
203 |
background: #1e3466;
|
204 |
-
// ! half
|
205 |
margin-top: 25px;
|
206 |
|
207 |
.custom-chart-header {
|
@@ -287,14 +292,3 @@
|
|
287 |
max-width: 100%;
|
288 |
}
|
289 |
}
|
290 |
-
|
291 |
-
:host ::ng-deep {
|
292 |
-
.progress-bar-stat-wrapper {
|
293 |
-
ngb-progressbar {
|
294 |
-
// ? Inner bar
|
295 |
-
.progress-bar {
|
296 |
-
border-radius: calc(14px/2);
|
297 |
-
}
|
298 |
-
}
|
299 |
-
}
|
300 |
-
}
|
71 |
|
72 |
.progress-bar-stat-wrapper {
|
73 |
--progress-bar-height: 20vw;
|
74 |
+
--progress-bar-width: 14px;
|
75 |
|
76 |
display: flex;
|
77 |
justify-content: center;
|
108 |
justify-content: center;
|
109 |
align-items: center;
|
110 |
|
111 |
+
ion-progress-bar {
|
112 |
+
transform: rotate(-90deg);
|
113 |
// ? As it is rotated, height references the actual width of the object
|
114 |
+
height: var(--progress-bar-width);
|
115 |
// ? Half the height
|
116 |
+
border-radius: calc(var(--progress-bar-width)/2);
|
|
|
|
|
|
|
|
|
117 |
// ? As it is rotated, width references the actual height of the object
|
118 |
width: var(--progress-bar-height);
|
119 |
+
|
120 |
+
&::part(track) {
|
121 |
+
background: #d5e7f8;
|
122 |
+
}
|
123 |
+
|
124 |
// ? Inner bar
|
125 |
+
&::part(progress) {
|
126 |
+
// ? Ionic scales the progress bar. When scaling, the border radius gets distorted (see: https://codepen.io/thebabydino/pen/VJQMmJ)
|
127 |
+
// ! A workaround could be found here: https://css-tricks.com/various-methods-for-expanding-a-box-while-preserving-the-border-radius/
|
128 |
+
border-radius: 0px;
|
129 |
+
background: #2c93e8;
|
130 |
}
|
131 |
}
|
132 |
}
|
194 |
}
|
195 |
|
196 |
.tag-label {
|
197 |
+
font-size: 16px;
|
198 |
text-overflow: unset;
|
199 |
white-space: unset;
|
200 |
overflow: initial;
|
206 |
padding: 20px 0px;
|
207 |
border-radius: 35px 35px 0px 0px;
|
208 |
background: #1e3466;
|
209 |
+
// ! half button height
|
210 |
margin-top: 25px;
|
211 |
|
212 |
.custom-chart-header {
|
292 |
max-width: 100%;
|
293 |
}
|
294 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,5 +1,5 @@
|
|
1 |
import { Component, OnInit, ViewChild } from "@angular/core";
|
2 |
-
import {
|
3 |
import { LineChartComponent } from "@swimlane/ngx-charts";
|
4 |
import { curveLinear, curveNatural } from "d3-shape";
|
5 |
|
@@ -14,7 +14,7 @@ import { CrmDashboardService } from "./crm-dashboard.service";
|
|
14 |
export class CrmDashboardPage implements OnInit {
|
15 |
LineChartComponent) chartInstance?: LineChartComponent;
(
|
16 |
|
17 |
-
customChartFiltersForm:
|
18 |
|
19 |
revenueChartData: any;
|
20 |
|
@@ -36,8 +36,8 @@ export class CrmDashboardPage implements OnInit {
|
|
36 |
constructor(
|
37 |
private crmDashboardService: CrmDashboardService
|
38 |
) {
|
39 |
-
this.customChartFiltersForm = new
|
40 |
-
selected_option: new
|
41 |
});
|
42 |
|
43 |
const isSmooth = true;
|
1 |
import { Component, OnInit, ViewChild } from "@angular/core";
|
2 |
+
import { UntypedFormControl, UntypedFormGroup } from "@angular/forms";
|
3 |
import { LineChartComponent } from "@swimlane/ngx-charts";
|
4 |
import { curveLinear, curveNatural } from "d3-shape";
|
5 |
|
14 |
export class CrmDashboardPage implements OnInit {
|
15 |
LineChartComponent) chartInstance?: LineChartComponent;
(
|
16 |
|
17 |
+
customChartFiltersForm: UntypedFormGroup;
|
18 |
|
19 |
revenueChartData: any;
|
20 |
|
36 |
constructor(
|
37 |
private crmDashboardService: CrmDashboardService
|
38 |
) {
|
39 |
+
this.customChartFiltersForm = new UntypedFormGroup({
|
40 |
+
selected_option: new UntypedFormControl('week')
|
41 |
});
|
42 |
|
43 |
const isSmooth = true;
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable } from '@angular/core';
|
2 |
+
import { CanActivate, Router } from '@angular/router';
|
3 |
+
|
4 |
+
import { Preferences } from '@capacitor/preferences';
|
5 |
+
|
6 |
+
()
|
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
|
15 |
+
this.router.navigate(['auth']);
|
16 |
+
return false;
|
17 |
+
} else {
|
18 |
+
return true;
|
19 |
+
}
|
20 |
+
}
|
21 |
+
}
|
@@ -10,11 +10,13 @@ import { SwiperModule } from 'swiper/angular';
|
|
10 |
import { ComponentsModule } from '../components/components.module';
|
11 |
|
12 |
import { WalkthroughPage } from './walkthrough.page';
|
|
|
13 |
|
14 |
const routes: Routes = [
|
15 |
{
|
16 |
path: '',
|
17 |
-
component: WalkthroughPage
|
|
|
18 |
}
|
19 |
];
|
20 |
|
@@ -27,6 +29,7 @@ const routes: Routes = [
|
|
27 |
ComponentsModule,
|
28 |
SwiperModule
|
29 |
],
|
30 |
-
declarations: [WalkthroughPage]
|
|
|
31 |
})
|
32 |
export class WalkthroughPageModule {}
|
10 |
import { ComponentsModule } from '../components/components.module';
|
11 |
|
12 |
import { WalkthroughPage } from './walkthrough.page';
|
13 |
+
import { WalkthroughGuard } from './walkthrough.guard';
|
14 |
|
15 |
const routes: Routes = [
|
16 |
{
|
17 |
path: '',
|
18 |
+
component: WalkthroughPage,
|
19 |
+
canActivate: [WalkthroughGuard]
|
20 |
}
|
21 |
];
|
22 |
|
29 |
ComponentsModule,
|
30 |
SwiperModule
|
31 |
],
|
32 |
+
declarations: [WalkthroughPage],
|
33 |
+
providers: [WalkthroughGuard]
|
34 |
})
|
35 |
export class WalkthroughPageModule {}
|
@@ -1,7 +1,9 @@
|
|
1 |
-
import { Component, AfterViewInit, ViewChild, HostBinding, NgZone } from '@angular/core';
|
|
|
2 |
|
3 |
-
import {
|
4 |
-
|
|
|
5 |
|
6 |
import SwiperCore, { Pagination } from 'swiper';
|
7 |
import { SwiperComponent } from 'swiper/angular';
|
@@ -17,7 +19,7 @@ SwiperCore.use([Pagination, IonicSwiper]);
|
|
17 |
'./styles/walkthrough.responsive.scss'
|
18 |
]
|
19 |
})
|
20 |
-
export class WalkthroughPage implements AfterViewInit {
|
21 |
swiperRef: SwiperCore;
|
22 |
|
23 |
SwiperComponent, { static: false }) swiper?: SwiperComponent;
(
|
@@ -27,10 +29,19 @@ export class WalkthroughPage implements AfterViewInit {
|
|
27 |
'class.last-slide-active') isLastSlide = false;
(
|
28 |
|
29 |
constructor(
|
|
|
30 |
public menu: MenuController,
|
31 |
private ngZone: NgZone
|
32 |
) { }
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
// Disable side menu for this page
|
35 |
ionViewDidEnter(): void {
|
36 |
this.menu.enable(false);
|
@@ -43,25 +54,25 @@ export class WalkthroughPage implements AfterViewInit {
|
|
43 |
|
44 |
ngAfterViewInit(): void {
|
45 |
// Accessing slides in server platform throw errors
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
54 |
});
|
55 |
-
});
|
56 |
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
62 |
});
|
63 |
-
}
|
64 |
-
// }
|
65 |
}
|
66 |
|
67 |
public setSwiperInstance(swiper: SwiperCore): void {
|
1 |
+
import { Inject, PLATFORM_ID, Component, AfterViewInit, ViewChild, HostBinding, NgZone, OnInit } from '@angular/core';
|
2 |
+
import { isPlatformBrowser } from '@angular/common';
|
3 |
|
4 |
+
import { Preferences } from '@capacitor/preferences';
|
5 |
+
|
6 |
+
import { MenuController, IonicSwiper } from '@ionic/angular';
|
7 |
|
8 |
import SwiperCore, { Pagination } from 'swiper';
|
9 |
import { SwiperComponent } from 'swiper/angular';
|
19 |
'./styles/walkthrough.responsive.scss'
|
20 |
]
|
21 |
})
|
22 |
+
export class WalkthroughPage implements AfterViewInit, OnInit {
|
23 |
swiperRef: SwiperCore;
|
24 |
|
25 |
SwiperComponent, { static: false }) swiper?: SwiperComponent;
(
|
29 |
'class.last-slide-active') isLastSlide = false;
(
|
30 |
|
31 |
constructor(
|
32 |
+
PLATFORM_ID) private platformId: object,
(
|
33 |
public menu: MenuController,
|
34 |
private ngZone: NgZone
|
35 |
) { }
|
36 |
|
37 |
+
ngOnInit(): void {
|
38 |
+
// save key to mark the walkthrough as visited so the next time the user visits the app, he would be redirected to log in
|
39 |
+
Preferences.set({
|
40 |
+
key: 'visitedWalkthrough',
|
41 |
+
value: 'true'
|
42 |
+
});
|
43 |
+
}
|
44 |
+
|
45 |
// Disable side menu for this page
|
46 |
ionViewDidEnter(): void {
|
47 |
this.menu.enable(false);
|
54 |
|
55 |
ngAfterViewInit(): void {
|
56 |
// Accessing slides in server platform throw errors
|
57 |
+
if (isPlatformBrowser(this.platformId)) {
|
58 |
+
this.swiperRef = this.swiper.swiperRef;
|
59 |
+
|
60 |
+
this.swiperRef.on('slidesLengthChange', () => {
|
61 |
+
// ? We need to use ngZone because the change happens outside Angular
|
62 |
+
// (see: https://swiperjs.com/angular#swiper-component-events)
|
63 |
+
this.ngZone.run(() => {
|
64 |
+
this.markSlides(this.swiperRef);
|
65 |
+
});
|
66 |
});
|
|
|
67 |
|
68 |
+
this.swiperRef.on('slideChange', () => {
|
69 |
+
// ? We need to use ngZone because the change happens outside Angular
|
70 |
+
// (see: https://swiperjs.com/angular#swiper-component-events)
|
71 |
+
this.ngZone.run(() => {
|
72 |
+
this.markSlides(this.swiperRef);
|
73 |
+
});
|
74 |
});
|
75 |
+
}
|
|
|
76 |
}
|
77 |
|
78 |
public setSwiperInstance(swiper: SwiperCore): void {
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
|
3 |
import { forkJoin, from, Observable } from 'rxjs';
|
4 |
import { map } from 'rxjs/operators';
|
5 |
|
6 |
-
import {
|
7 |
|
8 |
import { environment } from '../../environments/environment';
|
9 |
|
@@ -13,7 +13,7 @@ export class WordpressNativeService {
|
|
13 |
constructor() {}
|
14 |
|
15 |
public getPost(postId: any): Observable<any> {
|
16 |
-
const postPromise: Promise<HttpResponse> =
|
17 |
url: `${environment.wordpress_api_url}posts/${postId}`
|
18 |
});
|
19 |
|
@@ -33,7 +33,7 @@ export class WordpressNativeService {
|
|
33 |
// ? If you want to query posts by category
|
34 |
let category_url = categoryId ? '&categories=' + categoryId : '';
|
35 |
|
36 |
-
const recentPostsPromise: Promise<HttpResponse> =
|
37 |
url: environment.wordpress_api_url +
|
38 |
'posts?page=' +
|
39 |
page +
|
@@ -59,7 +59,7 @@ export class WordpressNativeService {
|
|
59 |
}
|
60 |
|
61 |
public getComments(postId: number, page: number = 1): Observable<any> {
|
62 |
-
const commentsPromise: Promise<HttpResponse> =
|
63 |
url: environment.wordpress_api_url + 'comments?post=' + postId + '&page=' + page
|
64 |
});
|
65 |
|
@@ -76,7 +76,7 @@ export class WordpressNativeService {
|
|
76 |
}
|
77 |
|
78 |
public getAuthor(author): Observable<any> {
|
79 |
-
const authorPromise: Promise<HttpResponse> =
|
80 |
url: environment.wordpress_api_url + 'users/' + author
|
81 |
});
|
82 |
|
@@ -105,7 +105,7 @@ export class WordpressNativeService {
|
|
105 |
}
|
106 |
|
107 |
public getCategory(category: number): Observable<any> {
|
108 |
-
const categoryPromise: Promise<HttpResponse> =
|
109 |
url: environment.wordpress_api_url + 'categories/' + category
|
110 |
});
|
111 |
|
3 |
import { forkJoin, from, Observable } from 'rxjs';
|
4 |
import { map } from 'rxjs/operators';
|
5 |
|
6 |
+
import { CapacitorHttp, HttpResponse } from '@capacitor/core';
|
7 |
|
8 |
import { environment } from '../../environments/environment';
|
9 |
|
13 |
constructor() {}
|
14 |
|
15 |
public getPost(postId: any): Observable<any> {
|
16 |
+
const postPromise: Promise<HttpResponse> = CapacitorHttp.get({
|
17 |
url: `${environment.wordpress_api_url}posts/${postId}`
|
18 |
});
|
19 |
|
33 |
// ? If you want to query posts by category
|
34 |
let category_url = categoryId ? '&categories=' + categoryId : '';
|
35 |
|
36 |
+
const recentPostsPromise: Promise<HttpResponse> = CapacitorHttp.get({
|
37 |
url: environment.wordpress_api_url +
|
38 |
'posts?page=' +
|
39 |
page +
|
59 |
}
|
60 |
|
61 |
public getComments(postId: number, page: number = 1): Observable<any> {
|
62 |
+
const commentsPromise: Promise<HttpResponse> = CapacitorHttp.get({
|
63 |
url: environment.wordpress_api_url + 'comments?post=' + postId + '&page=' + page
|
64 |
});
|
65 |
|
76 |
}
|
77 |
|
78 |
public getAuthor(author): Observable<any> {
|
79 |
+
const authorPromise: Promise<HttpResponse> = CapacitorHttp.get({
|
80 |
url: environment.wordpress_api_url + 'users/' + author
|
81 |
});
|
82 |
|
105 |
}
|
106 |
|
107 |
public getCategory(category: number): Observable<any> {
|
108 |
+
const categoryPromise: Promise<HttpResponse> = CapacitorHttp.get({
|
109 |
url: environment.wordpress_api_url + 'categories/' + category
|
110 |
});
|
111 |
|
@@ -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
|
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
|
@@ -7,4 +7,4 @@ if (environment.production) {
|
|
7 |
}
|
8 |
|
9 |
export { AppServerModule } from './app/app.server.module';
|
10 |
-
export {
|
7 |
}
|
8 |
|
9 |
export { AppServerModule } from './app/app.server.module';
|
10 |
+
export { renderModuleFactory } from '@angular/platform-server';
|
@@ -1,3 +1,7 @@
|
|
|
|
|
|
|
|
|
|
1 |
ion-title {
|
2 |
// Adding !important to force precedence in SSR
|
3 |
font-weight: 400 ;
|
1 |
+
html {
|
2 |
+
line-height: 1.5;
|
3 |
+
}
|
4 |
+
|
5 |
ion-title {
|
6 |
// Adding !important to force precedence in SSR
|
7 |
font-weight: 400 ;
|
@@ -115,6 +115,3 @@ html.ios {
|
|
115 |
html.md {
|
116 |
--app-header-height: 56px;
|
117 |
}
|
118 |
-
|
119 |
-
/* Importing Bootstrap SCSS file. */
|
120 |
-
@import '~bootstrap/scss/bootstrap';
|
115 |
html.md {
|
116 |
--app-header-height: 56px;
|
117 |
}
|
|
|
|
|
|
@@ -16,6 +16,5 @@
|
|
16 |
"src/**/*.d.ts"
|
17 |
],
|
18 |
"angularCompilerOptions": {
|
19 |
-
"enableIvy": true
|
20 |
}
|
21 |
}
|
16 |
"src/**/*.d.ts"
|
17 |
],
|
18 |
"angularCompilerOptions": {
|
|
|
19 |
}
|
20 |
}
|
@@ -3,7 +3,6 @@
|
|
3 |
"extends": "./tsconfig.app.json",
|
4 |
"compilerOptions": {
|
5 |
"outDir": "../out-tsc/server",
|
6 |
-
"target": "es2016",
|
7 |
// For SSR we need to include googlemaps typings
|
8 |
// (see: https://developers.google.com/maps/documentation/javascript/using-typescript#compiler_options_in_tsconfigjson_recommended)
|
9 |
"types": [
|
3 |
"extends": "./tsconfig.app.json",
|
4 |
"compilerOptions": {
|
5 |
"outDir": "../out-tsc/server",
|
|
|
6 |
// For SSR we need to include googlemaps typings
|
7 |
// (see: https://developers.google.com/maps/documentation/javascript/using-typescript#compiler_options_in_tsconfigjson_recommended)
|
8 |
"types": [
|
@@ -12,13 +12,14 @@
|
|
12 |
"allowSyntheticDefaultImports": true,
|
13 |
"emitDecoratorMetadata": true,
|
14 |
"experimentalDecorators": true,
|
15 |
-
"target": "
|
16 |
"typeRoots": [
|
17 |
"node_modules/@types"
|
18 |
],
|
19 |
"lib": [
|
20 |
"es2018",
|
21 |
"dom"
|
22 |
-
]
|
|
|
23 |
}
|
24 |
}
|
12 |
"allowSyntheticDefaultImports": true,
|
13 |
"emitDecoratorMetadata": true,
|
14 |
"experimentalDecorators": true,
|
15 |
+
"target": "ES2022",
|
16 |
"typeRoots": [
|
17 |
"node_modules/@types"
|
18 |
],
|
19 |
"lib": [
|
20 |
"es2018",
|
21 |
"dom"
|
22 |
+
],
|
23 |
+
"useDefineForClassFields": false
|
24 |
}
|
25 |
}
|