Diff to HTML by rtfpessoa

Files changed (54) show
.firebaserc CHANGED
@@ -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
- "dev-ion4fullpwa": {
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
- "12-2021-release": [
22
- "pro-ion4fullpwa"
23
- ],
24
- "06-2022-release": [
25
- "pro-ionic-6-full-app"
26
  ]
27
  }
28
  },
29
- "elite-ionic-6-full-app": {
30
  "hosting": {
31
- "11-2022-release": [
32
- "elite-ionic-6-full-app"
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
  }
DIFF.md CHANGED
@@ -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-version --diff-filter=ADCMRT ':!package-lock.json' ':!dist' ':!.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-version --diff-filter=ADCMRT -- android/ > diff/pro-vs-elite_android-changelog.diff
10
  ```
11
 
12
  *PRO vs ELITE (**ios config**)*
13
  ```bash
14
- git diff pro-version..elite-version --diff-filter=ADCMRT -- ios/ > diff/pro-vs-elite_ios-changelog.diff
15
  ```
16
 
17
- *Last PRO update (**12-2021**) vs Current PRO update (**06-2022**)*
 
 
18
  ```bash
19
- git diff 12-2021_pro-update..pro-version --diff-filter=ADCMRT ':!package-lock.json' ':!dist' ':!.gradle/*' ':!android' ':!ios' ':!*.diff' ':!*.png' ':!*.svg' > diff/last-pro-update-vs-current-pro_changelog.diff
20
  ```
21
 
22
- *Last PRO update (**12-2021**) vs Current PRO update (**06-2022**) (**android config**)*
23
  ```bash
24
- git diff 12-2021_pro-update..pro-version --diff-filter=ADCMRT -- android/ > diff/last-pro-update-vs-current-pro_android-changelog.diff
25
  ```
26
 
27
- *Last PRO update (**12-2021**) vs Current PRO update (**06-2022**) (**ios config**)*
28
  ```bash
29
- git diff 12-2021_pro-update..pro-version --diff-filter=ADCMRT -- ios/ > diff/last-pro-update-vs-current-pro_ios-changelog.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-pro-update-vs-current-pro_changelog.html --input file -- diff/last-pro-update-vs-current-pro_changelog.diff
42
 
43
- diff2html --style side --file diff/previews/last-pro-update-vs-current-pro_android-changelog.html --input file -- diff/last-pro-update-vs-current-pro_android-changelog.diff
44
 
45
- diff2html --style side --file diff/previews/last-pro-update-vs-current-pro_ios-changelog.html --input file -- diff/last-pro-update-vs-current-pro_ios-changelog.diff
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-pro-update-vs-current-pro_changelog.diff
57
 
58
- diff2html --style side --input file -- diff/last-pro-update-vs-current-pro_android-changelog.diff
59
 
60
- diff2html --style side --input file -- diff/last-pro-update-vs-current-pro_ios-changelog.diff
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
  ```
FIREBASE.md CHANGED
@@ -1,23 +1,24 @@
1
- # Prepare assets to deploy
 
 
2
  Before deploying the app to Firebase Hosting, run `ionic build --prod`
3
 
4
- # Multiple Environments with Firebase
5
  For managing one site across different environments, we recommend multiple projects for promoting best practices of each environment having its own set of Firebase resources.
6
 
7
- For example for this repo we will have two firebase projects:
8
  - dev-ion4fullpwa
9
- - ion4fullpwa
10
 
11
- ## Check available alias
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
- prod (ion4fullpwa)
17
- pro (pro-ion4fullpwa)
18
  ```
19
 
20
- ## Create alias
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
- ## Select alias (switching environments)
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
- ## Serve and test your Firebase project locally
39
  For more info see: https://firebase.google.com/docs/hosting/deploying
40
  `firebase serve --only hosting`
41
 
42
  ---
43
 
44
- # [Advanced uses](https://firebase.google.com/docs/cli/targets#deploy-target-commands)
45
 
46
- ## [Create target](https://firebase.google.com/docs/cli/targets#set-up-deploy-target-hosting)
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
- ## [Configure your `firebase.json` file to use deploy targets](https://firebase.google.com/docs/cli/targets#configure_your_firebasejson_file_to_use_deploy_targets)
67
  Don't forget to configure this before deploying to the new target
68
 
69
 
70
- ## Deploy to specific target (i.e.: different site)
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
- ## Create channel
81
- - CHANNEL_ID = dev
 
 
 
82
  ``` bash
83
- firebase hosting:channel:deploy dev --only ionic-6 -P dev
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]
README.md CHANGED
@@ -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 ios folder which contains the iOS native app.
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 PRO 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
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
SCAFFOLDING.md CHANGED
@@ -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
UPDATE.md CHANGED
@@ -1,4 +1,50 @@
1
- ### Update Ionic dependencies
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ```
angular.json CHANGED
@@ -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
- "defaultCollection": "@angular-eslint/schematics"
 
 
 
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
+ }
firebase.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "hosting": [
3
  {
4
- "target": "11-2022-release",
5
  "public": "dist/app/browser",
6
  "ignore": [
7
  "firebase.json",
@@ -43,49 +43,7 @@
43
  ]
44
  },
45
  {
46
- "target": "06-2022-release",
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",
package.json CHANGED
@@ -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.1",
5
  "author": "IonicThemes",
6
  "contributors": [
7
  "Dayana <dayana@ionicthemes.com>",
@@ -21,85 +21,82 @@
21
  "postinstall": "jetifier"
22
  },
23
  "dependencies": {
24
- "@angular/animations": "^13.1.1",
25
- "@angular/cdk": "^13.3.0",
26
- "@angular/common": "^13.1.1",
27
- "@angular/core": "^13.1.1",
28
- "@angular/fire": "^7.3.0",
29
- "@angular/forms": "^13.1.1",
30
- "@angular/localize": "^13.1.1",
31
- "@angular/platform-browser": "^13.1.1",
32
- "@angular/platform-browser-dynamic": "^13.1.1",
33
- "@angular/platform-server": "^13.1.1",
34
- "@angular/router": "^13.1.1",
35
- "@angular/service-worker": "^13.1.1",
36
- "@capacitor-community/http": "^1.4.1",
37
- "@capacitor-firebase/authentication": "^0.3.1",
38
- "@capacitor/android": "^3.5.1",
39
- "@capacitor/app": "^1.1.1",
40
- "@capacitor/core": "^3.5.1",
41
- "@capacitor/geolocation": "^1.3.1",
42
- "@capacitor/haptics": "^1.1.4",
43
- "@capacitor/ios": "^3.5.1",
44
- "@capacitor/keyboard": "^1.2.2",
45
- "@capacitor/share": "^1.1.2",
46
- "@capacitor/splash-screen": "^1.2.2",
47
- "@capacitor/status-bar": "^1.0.8",
48
- "@ionic/angular": "^6.3.5",
49
- "@ionic/angular-server": "^6.3.5",
50
- "@ng-bootstrap/ng-bootstrap": "^12.1.2",
51
- "@nguniversal/express-engine": "^13.0.1",
52
  "@ngx-translate/core": "^14.0.0",
53
  "@ngx-translate/http-loader": "^7.0.0",
54
- "@popperjs/core": "^2.10.2",
55
- "@swimlane/ngx-charts": "^20.1.0",
56
- "@videogular/ngx-videogular": "^5.0.1",
57
  "angular-pipes": "^10.0.0",
58
- "bootstrap": "^5.1.3",
59
- "core-js": "^3.20.0",
60
- "date-fns": "^2.27.0",
61
- "dayjs": "^1.11.2",
62
- "express": "^4.17.2",
63
- "firebase": "^9.8.2",
64
- "google-libphonenumber": "^3.2.27",
65
  "jetifier": "^2.0.0",
66
  "mobile-detect": "^1.4.5",
67
- "rxjs": "^7.4.0",
68
- "swiper": "^8.2.2",
69
- "tslib": "^2.0.0",
70
- "zone.js": "~0.11.4"
71
  },
72
  "devDependencies": {
73
- "@angular-devkit/architect": "0.1301.2",
74
- "@angular-devkit/build-angular": "~13.1.2",
75
- "@angular-devkit/core": "^13.1.2",
76
- "@angular-devkit/schematics": "^13.1.2",
77
- "@angular-eslint/builder": "13.0.1",
78
- "@angular-eslint/eslint-plugin": "13.0.1",
79
- "@angular-eslint/eslint-plugin-template": "13.0.1",
80
- "@angular-eslint/schematics": "13.0.1",
81
- "@angular-eslint/template-parser": "13.0.1",
82
- "@angular/cli": "~13.1.2",
83
- "@angular/compiler": "^13.1.1",
84
- "@angular/compiler-cli": "^13.1.1",
85
- "@angular/language-service": "~13.1.1",
86
- "@capacitor/cli": "^3.5.1",
87
- "@commitlint/cli": "^17.0.2",
88
- "@commitlint/config-angular": "^17.0.0",
89
  "@ionic/angular-toolkit": "^7.0.0",
90
- "@ionic/cli": "6.20.3",
91
- "@nguniversal/builders": "^13.0.1",
92
  "@types/core-js": "^2.5.5",
93
- "@types/express": "^4.17.13",
94
  "@types/googlemaps": "^3.39.2",
95
- "@types/node": "^17.0.1",
96
- "@typescript-eslint/eslint-plugin": "^5.27.0",
97
- "@typescript-eslint/parser": "^5.27.0",
98
- "@webcomponents/webcomponentsjs": "^2.6.0",
99
  "cordova-res": "0.15.4",
100
- "eslint": "^8.16.0",
101
  "husky": "^4.3.8",
102
- "ts-node": "^10.4.0",
103
- "typescript": "~4.5.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
  }
src/app/activity/activities-filter-modal/activities-filter.modal.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, Input, OnInit } from '@angular/core';
2
- import { FormControl, FormGroup } from '@angular/forms';
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
  @Input() currentSelectedActivities: Array<ActivityCategory>;
16
 
17
- filterActivitiesForm: FormGroup;
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 FormControl(c.value);
34
  }
35
  );
36
 
37
- this.filterActivitiesForm = new FormGroup({
38
- activities: new FormGroup(activitiesControls),
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 FormGroup;
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 FormGroup;
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 FormGroup;
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
  @Input() 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(
src/app/activity/activity.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component } from '@angular/core';
2
- import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
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: FormGroup;
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 FormControl(c.value);
46
  }
47
  );
48
 
49
- this.filterActivitiesForm = new FormGroup({
50
- activities: new FormGroup(activitiesControls),
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 FormGroup;
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(
src/app/app-routing.module.ts CHANGED
@@ -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: 'enabled',
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
  })
src/app/app.component.html CHANGED
@@ -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>
src/app/app.component.ts CHANGED
@@ -1,9 +1,14 @@
1
  import { Component } from '@angular/core';
 
 
2
  import { SplashScreen } from '@capacitor/splash-screen';
3
- import { SeoService } from './utils/seo/seo.service';
 
4
  import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
 
5
  import { HistoryHelperService } from './utils/history-helper.service';
6
 
 
7
  @Component({
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 seoService: SeoService
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
  @Component({
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
  }
src/app/app.module.ts CHANGED
@@ -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 },
src/app/components/countdown-timer/countdown-timer.component.ts CHANGED
@@ -87,7 +87,7 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
87
  }
88
 
89
  constructor(
90
- @Inject(PLATFORM_ID) private platformId: object,
91
  ) { }
92
 
93
  ngOnInit(): void {
87
  }
88
 
89
  constructor(
90
+ @Inject(PLATFORM_ID) private platformId: object
91
  ) { }
92
 
93
  ngOnInit(): void {
src/app/components/counter-input/counter-input.component.ts CHANGED
@@ -1,8 +1,8 @@
1
  import { Component, forwardRef, Input, OnChanges, ViewEncapsulation } from '@angular/core';
2
- import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
3
 
4
  export function counterRangeValidator(minValue, maxValue) {
5
- return (c: FormControl) => {
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: FormControl) {
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
  }
src/app/contacts/contacts.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component } from '@angular/core';
2
- import { FormControl, FormGroup, Validators } from '@angular/forms';
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: FormGroup;
15
 
16
  currentContactsFilter: string;
17
 
18
  filteredContacts: Array<ContactsGroup>;
19
 
20
  constructor(private contactsService: ContactsService) {
21
- this.contactsFiltersForm = new FormGroup({
22
- contactsFilter: new FormControl('birthday', Validators.required),
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
src/app/firebase/auth/firebase-auth.helper.ts ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+
3
+ import { FacebookAuthProvider, getAdditionalUserInfo, GoogleAuthProvider, OAuthProvider, TwitterAuthProvider } from '@angular/fire/auth';
4
+ import {
5
+ AuthCredential as FirebaseAuthCredential,
6
+ AuthProvider as FirebaseAuthProvider,
7
+ CustomParameters as FirebaseCustomParameters,
8
+ OAuthCredential,
9
+ User as FirebaseUser,
10
+ UserCredential as FirebaseUserCredential
11
+ } from '@angular/fire/auth';
12
+
13
+ import { AuthCredential, SignInResult, User, AdditionalUserInfo, SignInOptions } from '@capacitor-firebase/authentication';
14
+
15
+
16
+ // * Aux methods inspired on the @capacitor-firebase/authentication library
17
+ @Injectable()
18
+ export class FirebaseAuthHelper {
19
+
20
+ public applySignInOptions(options: SignInOptions, provider: OAuthProvider | GoogleAuthProvider | FacebookAuthProvider | TwitterAuthProvider) {
21
+ if (options.customParameters) {
22
+ const customParameters: FirebaseCustomParameters = {};
23
+ options.customParameters.map(parameter => {
24
+ customParameters[parameter.key] = parameter.value;
25
+ });
26
+ provider.setCustomParameters(customParameters);
27
+ }
28
+ if (options.scopes) {
29
+ for (const scope of options.scopes) {
30
+ provider.addScope(scope);
31
+ }
32
+ }
33
+ }
34
+
35
+ // ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L594)
36
+ public createSignInResult(userCredential: FirebaseUserCredential | null, authCredential: FirebaseAuthCredential | null): SignInResult {
37
+ const userResult = this.createUserResult(userCredential?.user || null);
38
+ const credentialResult = this.createCredentialResult(authCredential);
39
+ const additionalUserInfoResult = this.createAdditionalUserInfoResult(userCredential);
40
+ const result: SignInResult = {
41
+ user: userResult,
42
+ credential: credentialResult,
43
+ additionalUserInfo: additionalUserInfoResult
44
+ };
45
+ return result;
46
+ }
47
+
48
+ // ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L627)
49
+ public createUserResult(user: FirebaseUser | null): User | null {
50
+ if (!user) {
51
+ return null;
52
+ }
53
+ const result: User = {
54
+ displayName: user.displayName,
55
+ email: user.email,
56
+ emailVerified: user.emailVerified,
57
+ isAnonymous: user.isAnonymous,
58
+ phoneNumber: user.phoneNumber,
59
+ photoUrl: user.providerData[0].photoURL,
60
+ providerId: user.providerData[0].providerId,
61
+ tenantId: user.tenantId,
62
+ uid: user.uid
63
+ };
64
+ return result;
65
+ }
66
+
67
+ // ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L610)
68
+ private createCredentialResult(credential: FirebaseAuthCredential | null): AuthCredential | null {
69
+ if (!credential) {
70
+ return null;
71
+ }
72
+ const result: AuthCredential = {
73
+ providerId: credential.providerId
74
+ };
75
+ if (credential instanceof OAuthCredential) {
76
+ result.accessToken = credential.accessToken;
77
+ result.idToken = credential.idToken;
78
+ result.secret = credential.secret;
79
+ }
80
+ return result;
81
+ }
82
+
83
+ // ? (see: https://github.com/capawesome-team/capacitor-firebase/blob/9024eef856dbd25b2b6459e4b6bcee104ca89755/packages/authentication/src/web.ts#L645)
84
+ private createAdditionalUserInfoResult(credential: FirebaseUserCredential | null): AdditionalUserInfo | null {
85
+ if (!credential) {
86
+ return null;
87
+ }
88
+ const additionalUserInfo = getAdditionalUserInfo(credential);
89
+ if (!additionalUserInfo) {
90
+ return null;
91
+ }
92
+ const { isNewUser, profile, providerId, username } = additionalUserInfo;
93
+ const result: AdditionalUserInfo = {
94
+ isNewUser
95
+ };
96
+ if (providerId !== null) {
97
+ result.providerId = providerId;
98
+ }
99
+ if (profile !== null) {
100
+ result.profile = profile as { [key: string]: unknown };
101
+ }
102
+ if (username !== null && username !== undefined) {
103
+ result.username = username;
104
+ }
105
+ return result;
106
+ }
107
+ }
src/app/firebase/auth/firebase-auth.module.ts CHANGED
@@ -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: [FirebaseAuthService]
 
 
 
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 {}
src/app/firebase/auth/firebase-auth.service.ts CHANGED
@@ -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
- AuthCredential as FirebaseAuthCredential,
13
- User as FirebaseUser,
14
  } from '@angular/fire/auth';
15
 
16
- import { AuthCredential, AuthStateChange, FirebaseAuthentication, SignInResult, User } from '@capacitor-firebase/authentication';
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
  @Injectable({
@@ -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
  @Inject(PLATFORM_ID) private platformId: object
@@ -90,7 +91,7 @@ export class FirebaseAuthService implements OnDestroy {
90
  break;
91
  }
92
 
93
- const signInResult = this.createSignInResult(result.user, credential);
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: AuthProvider, scopes?: Array<string>): Promise<SignInResult> {
160
  this.presentLoading(provider.providerId);
161
 
162
  let authResult: SignInResult = null;
163
 
164
  if (this.platform.is('capacitor')) {
165
- authResult = await this.nativeAuth(provider, scopes);
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 prepareForAuthWithProvidersRedirection(authProviderId: string): void {
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.user, webCredential);
269
  } else {
270
  return Promise.reject('null webAuthUserCredential');
271
  }
272
  }
273
 
274
- private async nativeAuth(provider: AuthProvider, scopes?: Array<string>): Promise<SignInResult> {
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(nativeAuthResult.credential?.idToken, nativeAuthResult.credential?.accessToken);
 
 
 
318
  break;
319
  case SignInProvider.twitter:
320
  try {
321
- nativeCredential = TwitterAuthProvider.credential(nativeAuthResult.credential?.accessToken, nativeAuthResult.credential?.secret);
 
 
 
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.user, nativeCredential);
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 scopes = ['email'];
 
 
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, scopes);
343
  }
344
 
345
  public async signInWithGoogle(): Promise<SignInResult> {
346
  const provider = new GoogleAuthProvider();
347
- const scopes = ['profile', 'email'];
 
 
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, scopes);
351
  }
352
 
353
  public async signInWithTwitter(): Promise<SignInResult> {
354
  const provider = new TwitterAuthProvider();
355
- const scopes = ['name', 'email'];
 
 
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, scopes);
359
  }
360
 
361
  public async signInWithApple(): Promise<SignInResult> {
362
  const provider = new OAuthProvider('apple.com');
363
- const scopes = ['name', 'email'];
 
 
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, scopes);
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.createSignInResultFromUserCredential(credential);
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.createSignInResultFromUserCredential(credential);
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
  @Injectable({
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
  @Inject(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
  }
src/app/firebase/auth/sign-in/firebase-sign-in.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, NgZone } from '@angular/core';
2
- import { Validators, FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
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 FormGroup({
41
- 'email': new FormControl('', Validators.compose([
42
  Validators.required,
43
  Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
44
  ])),
45
- 'password': new FormControl('', Validators.compose([
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
  ]))
src/app/firebase/auth/sign-up/firebase-sign-up.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, OnInit, NgZone } from '@angular/core';
2
- import { Validators, FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
23
- matching_passwords_group: FormGroup;
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 FormGroup({
52
- 'password': new FormControl('', Validators.compose([
53
  Validators.minLength(6),
54
  Validators.required
55
  ])),
56
- 'confirm_password': new FormControl('', Validators.required)
57
- }, (formGroup: FormGroup) => {
58
  return PasswordValidator.areNotEqual(formGroup);
59
  });
60
 
61
- this.signupForm = new FormGroup({
62
- 'email': new FormControl('', Validators.compose([
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
  ])),
src/app/firebase/crud/listing/firebase-listing.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, OnInit, OnDestroy, HostBinding } from '@angular/core';
2
- import { FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
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 FormGroup({
58
- dual: new FormControl({lower: 1, upper: 100})
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
src/app/firebase/crud/user/create/firebase-create-user.modal.ts CHANGED
@@ -1,6 +1,6 @@
1
  import { Component, Input, OnInit } from '@angular/core';
2
  import { ModalController } from '@ionic/angular';
3
- import { Validators, FormGroup, FormControl, FormArray } from '@angular/forms';
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
  @Input() modalId: string;
25
 
26
- createUserForm: FormGroup;
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 FormGroup({
41
- name: new FormControl('', Validators.required),
42
- lastname: new FormControl('', Validators.required),
43
- email: new FormControl('', Validators.compose([
44
  Validators.required,
45
  Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
46
  ])),
47
- phone: new FormControl('', Validators.required),
48
- birthdate: new FormControl(null, Validators.required),
49
- skills: new FormArray([], CheckboxCheckedValidator.minSelectedCheckboxes(1)),
50
- spanish: new FormControl(),
51
- english: new FormControl(),
52
- french: new FormControl()
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 FormArray).push(new FormControl());
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 <FormArray>this.createUserForm.get('skills'); }
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
  @Input() 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) {
src/app/firebase/crud/user/update/firebase-update-user.modal.ts CHANGED
@@ -1,6 +1,6 @@
1
  import { Component, OnInit, Input, NgZone } from '@angular/core';
2
  import { ModalController, AlertController } from '@ionic/angular';
3
- import { Validators, FormGroup, FormControl, FormArray } from '@angular/forms';
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
  @Input() modalId: string;
30
 
31
- updateUserForm: FormGroup;
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 FormGroup({
48
- name: new FormControl(this.user.name, Validators.required),
49
- lastname: new FormControl(this.user.lastname, Validators.required),
50
- email: new FormControl(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 FormControl(this.user.phone, Validators.required),
55
- birthdate: new FormControl(dayjs.unix(this.user.birthdate).format(), Validators.required),
56
- skills: new FormArray([], CheckboxCheckedValidator.minSelectedCheckboxes(1)),
57
- spanish: new FormControl(this.user.languages.spanish),
58
- english: new FormControl(this.user.languages.english),
59
- french: new FormControl(this.user.languages.french)
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 FormControl(userSkillsIds.includes(skill.id));
76
- (this.updateUserForm.controls.skills as FormArray).push(control);
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 <FormArray>this.updateUserForm.get('skills'); }
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
  @Input() 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) {
src/app/forgot-password/forgot-password.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component } from '@angular/core';
2
- import { Validators, FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
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 FormGroup({
28
- 'email': new FormControl('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
  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
  ]))
src/app/forms/filters/forms-filters.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component} from '@angular/core';
2
- import { FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
17
- radioForm: FormGroup;
18
- checkboxTagsForm: FormGroup;
19
- radioTagsForm: FormGroup;
20
- switchersForm: FormGroup;
21
  counterForm: any;
22
- ratingForm: FormGroup;
23
- radioColorForm: FormGroup;
24
 
25
  constructor() {
26
- this.rangeForm = new FormGroup({
27
- single: new FormControl(25),
28
- dual: new FormControl({lower: 12, upper: 23})
29
  });
30
 
31
- this.checkboxForm = new FormGroup({
32
- person_1: new FormControl(true),
33
- person_2: new FormControl(false),
34
- person_3: new FormControl(false),
35
- person_4: new FormControl(true),
36
- person_5: new FormControl(false)
37
  });
38
 
39
- this.radioForm = new FormGroup({
40
- selected_option: new FormControl('apple')
41
  });
42
 
43
- this.checkboxTagsForm = new FormGroup({
44
- tag_1: new FormControl(true),
45
- tag_2: new FormControl(false),
46
- tag_3: new FormControl(true),
47
- tag_4: new FormControl(true),
48
- tag_5: new FormControl(false),
49
- tag_6: new FormControl(false),
50
- tag_7: new FormControl({value: true, disabled: true}),
51
- tag_8: new FormControl(false)
52
  });
53
 
54
- this.radioTagsForm = new FormGroup({
55
- selected_option: new FormControl('any')
56
  });
57
 
58
- this.switchersForm = new FormGroup({
59
- notifications: new FormControl(true),
60
- email_notifications: new FormControl(false)
61
  });
62
 
63
- this.counterForm = new FormGroup({
64
- counter: new FormControl(5, counterRangeValidator(1, 7)),
65
- counter2: new FormControl(2, counterRangeValidator(1, 5))
66
  });
67
 
68
- this.ratingForm = new FormGroup({
69
- rate: new FormControl(2.5),
70
- rate2: new FormControl(1.5)
71
  });
72
 
73
- this.radioColorForm = new FormGroup({
74
- selected_color: new FormControl('#fc9961')
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
 
src/app/forms/validations/forms-validations.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, OnInit } from '@angular/core';
2
- import { Validators, FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
21
- matching_passwords_group: FormGroup;
22
- country_phone_group: FormGroup;
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 FormGroup({
90
- password: new FormControl('', 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 FormControl('', Validators.required)
96
- }, (formGroup: FormGroup) => {
97
  return PasswordValidator.areNotEqual(formGroup);
98
  });
99
 
100
- const country = new FormControl(this.countries[0], Validators.required);
101
 
102
- const phone = new FormControl('', Validators.compose([
103
  Validators.required,
104
  PhoneValidator.invalidCountryPhone(country)
105
  ]));
106
- this.country_phone_group = new FormGroup({
107
  country: country,
108
  phone: phone
109
  });
110
 
111
- this.validationsForm = new FormGroup({
112
- 'username': new FormControl('', 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 FormControl('', Validators.required),
120
- 'lastname': new FormControl('', Validators.required),
121
- 'email': new FormControl('', Validators.compose([
122
  Validators.required,
123
  Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
124
  ])),
125
- 'gender': new FormControl(this.genders[0], Validators.required),
126
  'country_phone': this.country_phone_group,
127
  'matching_passwords': this.matching_passwords_group,
128
- 'guests': new FormControl(6, counterRangeValidator(1, 12)),
129
- 'bedrooms': new FormControl(3, counterRangeValidator(1, 5)),
130
- 'terms': new FormControl(true, Validators.pattern('true'))
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
 
src/app/getting-started/getting-started.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, HostBinding, NgZone } from '@angular/core';
2
- import { FormGroup, FormControl } from '@angular/forms';
3
 
4
  import { MenuController } from '@ionic/angular';
5
  import { IonicSwiper } from "@ionic/angular";
@@ -21,21 +21,21 @@ export class GettingStartedPage {
21
  @HostBinding('class.last-slide-active') isLastSlide = false;
22
 
23
  swiperRef: SwiperCore;
24
- gettingStartedForm: FormGroup;
25
 
26
  constructor(
27
  public menu: MenuController,
28
  private ngZone: NgZone
29
  ) {
30
- this.gettingStartedForm = new FormGroup({
31
- browsingCategory: new FormControl('men'),
32
- followingInterests: new FormGroup({
33
- tops: new FormControl(true),
34
- dresses: new FormControl(),
35
- jeans: new FormControl(),
36
- jackets: new FormControl(true),
37
- shoes: new FormControl(),
38
- glasses: new FormControl()
39
  })
40
  });
41
  }
1
  import { Component, HostBinding, NgZone } from '@angular/core';
2
+ import { UntypedFormGroup, UntypedFormControl } from '@angular/forms';
3
 
4
  import { MenuController } from '@ionic/angular';
5
  import { IonicSwiper } from "@ionic/angular";
21
  @HostBinding('class.last-slide-active') isLastSlide = false;
22
 
23
  swiperRef: SwiperCore;
24
+ gettingStartedForm: UntypedFormGroup;
25
 
26
  constructor(
27
  public menu: MenuController,
28
  private ngZone: NgZone
29
  ) {
30
+ this.gettingStartedForm = new UntypedFormGroup({
31
+ browsingCategory: new UntypedFormControl('men'),
32
+ followingInterests: new UntypedFormGroup({
33
+ tops: new UntypedFormControl(true),
34
+ dresses: new UntypedFormControl(),
35
+ jeans: new UntypedFormControl(),
36
+ jackets: new UntypedFormControl(true),
37
+ shoes: new UntypedFormControl(),
38
+ glasses: new UntypedFormControl()
39
  })
40
  });
41
  }
src/app/login/login.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component } from '@angular/core';
2
- import { Validators, FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
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 FormGroup({
32
- 'email': new FormControl('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 FormControl('', Validators.compose([
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
  ]))
src/app/showcase/custom-components/custom-components.module.ts CHANGED
@@ -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: '',
src/app/showcase/showcase.module.ts CHANGED
@@ -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
  ],
src/app/signup/signup.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component } from '@angular/core';
2
- import { Validators, FormGroup, FormControl } from '@angular/forms';
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: FormGroup;
19
- matching_passwords_group: FormGroup;
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 FormGroup({
45
- 'password': new FormControl('', Validators.compose([
46
  Validators.minLength(5),
47
  Validators.required
48
  ])),
49
- 'confirm_password': new FormControl('', Validators.required)
50
- }, (formGroup: FormGroup) => {
51
  return PasswordValidator.areNotEqual(formGroup);
52
  });
53
 
54
- this.signupForm = new FormGroup({
55
- 'email': new FormControl('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
  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
  ])),
src/app/validators/checkbox-checked.validator.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { ValidatorFn, FormArray } from '@angular/forms';
2
 
3
  export class CheckboxCheckedValidator {
4
 
5
  static minSelectedCheckboxes(min) {
6
- const validator: ValidatorFn = (formArray: 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)
src/app/validators/password.validator.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { FormControl, FormGroup } 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: 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: FormControl = <FormControl>formGroup.controls[key];
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;
src/app/validators/username.validator.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { FormControl } from '@angular/forms';
2
 
3
  export class UsernameValidator {
4
 
5
- static usernameNotAvailable(fc: FormControl) {
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.
src/app/visualizations/charts/charts-shared.module.ts CHANGED
@@ -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
  @NgModule({
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
  @NgModule({
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 { }
src/app/visualizations/charts/custom-progress-bar-chart/custom-progress-bar.directive.ts ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Directive, Input, OnInit } from '@angular/core';
2
+ import { IonProgressBar } from '@ionic/angular';
3
+
4
+ @Directive({
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
+ @Input()
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
+ @Input()
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
+ }
src/app/visualizations/crm-dashboard/crm-dashboard.module.ts CHANGED
@@ -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
src/app/visualizations/crm-dashboard/crm-dashboard.page.html CHANGED
@@ -46,14 +46,14 @@
46
  <div class="progress-bar-stat-wrapper">
47
  <div class="progress-bar-stat">
48
  <div class="progress-bar-container">
49
- <ngb-progressbar [value]="79" [max]="254"></ngb-progressbar>
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
- <ngb-progressbar [value]="18" [max]="100"></ngb-progressbar>
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>
src/app/visualizations/crm-dashboard/crm-dashboard.page.scss CHANGED
@@ -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
- ngb-progressbar {
 
111
  // ? As it is rotated, height references the actual width of the object
112
- --bs-progress-height: 14px;
113
  // ? Half the height
114
- --bs-progress-border-radius: calc(14px/2);
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
- .progress-bar {
124
- border-radius: calc(14px/2) !important;
 
 
 
125
  }
126
  }
127
  }
@@ -189,7 +194,7 @@
189
  }
190
 
191
  .tag-label {
192
- font-size: 14px;
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 of button height
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
  }
 
 
 
 
 
 
 
 
 
 
 
src/app/visualizations/crm-dashboard/crm-dashboard.page.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { Component, OnInit, ViewChild } from "@angular/core";
2
- import { FormControl, FormGroup } from "@angular/forms";
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
  @ViewChild(LineChartComponent) chartInstance?: LineChartComponent;
16
 
17
- customChartFiltersForm: FormGroup;
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 FormGroup({
40
- selected_option: new FormControl('week')
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
  @ViewChild(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;
src/app/walkthrough/walkthrough.guard.ts ADDED
@@ -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
+ @Injectable()
7
+ export class WalkthroughGuard implements CanActivate {
8
+ constructor(private router: Router) {}
9
+
10
+ async canActivate(): Promise<boolean> {
11
+ const { value } = await Preferences.get({ key: 'visitedWalkthrough' });
12
+
13
+ if (value === 'true') {
14
+ // this is a returning user, don't show him the walkthrough
15
+ this.router.navigate(['auth']);
16
+ return false;
17
+ } else {
18
+ return true;
19
+ }
20
+ }
21
+ }
src/app/walkthrough/walkthrough.module.ts CHANGED
@@ -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 {}
src/app/walkthrough/walkthrough.page.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { Component, AfterViewInit, ViewChild, HostBinding, NgZone } from '@angular/core';
 
2
 
3
- import { MenuController } from '@ionic/angular';
4
- import { IonicSwiper } from '@ionic/angular';
 
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
  @ViewChild(SwiperComponent, { static: false }) swiper?: SwiperComponent;
@@ -27,10 +29,19 @@ export class WalkthroughPage implements AfterViewInit {
27
  @HostBinding('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
- // if (isPlatformBrowser(this.platformId)) {
47
- this.swiperRef = this.swiper.swiperRef;
48
-
49
- this.swiperRef.on('slidesLengthChange', () => {
50
- // ? We need to use ngZone because the change happens outside Angular
51
- // (see: https://swiperjs.com/angular#swiper-component-events)
52
- this.ngZone.run(() => {
53
- this.markSlides(this.swiperRef);
 
54
  });
55
- });
56
 
57
- this.swiperRef.on('slideChange', () => {
58
- // ? We need to use ngZone because the change happens outside Angular
59
- // (see: https://swiperjs.com/angular#swiper-component-events)
60
- this.ngZone.run(() => {
61
- this.markSlides(this.swiperRef);
 
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
  @ViewChild(SwiperComponent, { static: false }) swiper?: SwiperComponent;
29
  @HostBinding('class.last-slide-active') isLastSlide = false;
30
 
31
  constructor(
32
+ @Inject(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 {
src/app/wordpress/wordpress.native.service.ts CHANGED
@@ -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 { Http, HttpResponse } from '@capacitor-community/http';
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> = Http.get({
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> = Http.get({
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> = Http.get({
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> = Http.get({
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> = Http.get({
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
 
src/global.scss CHANGED
@@ -12,10 +12,10 @@
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
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
src/main.server.ts CHANGED
@@ -7,4 +7,4 @@ if (environment.production) {
7
  }
8
 
9
  export { AppServerModule } from './app/app.server.module';
10
- export { renderModule, renderModuleFactory } from '@angular/platform-server';
7
  }
8
 
9
  export { AppServerModule } from './app/app.server.module';
10
+ export { renderModuleFactory } from '@angular/platform-server';
src/theme/app-defaults.scss CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  ion-title {
2
  // Adding !important to force precedence in SSR
3
  font-weight: 400 !important;
1
+ html {
2
+ line-height: 1.5;
3
+ }
4
+
5
  ion-title {
6
  // Adding !important to force precedence in SSR
7
  font-weight: 400 !important;
src/theme/variables.scss CHANGED
@@ -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
  }
 
 
 
src/tsconfig.app.json CHANGED
@@ -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
  }
src/tsconfig.server.json CHANGED
@@ -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": [
tsconfig.json CHANGED
@@ -12,13 +12,14 @@
12
  "allowSyntheticDefaultImports": true,
13
  "emitDecoratorMetadata": true,
14
  "experimentalDecorators": true,
15
- "target": "es2015",
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
  }