├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── angular.json
├── browserslist
├── ionic.config.json
├── package.json
├── src
├── app
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── app.scss
│ ├── interfaces
│ │ └── user-options.ts
│ ├── pages
│ │ ├── about-popover
│ │ │ └── about-popover.ts
│ │ ├── about
│ │ │ ├── about-routing.module.ts
│ │ │ ├── about.html
│ │ │ ├── about.module.ts
│ │ │ ├── about.scss
│ │ │ └── about.ts
│ │ ├── account
│ │ │ ├── account-routing.module.ts
│ │ │ ├── account.html
│ │ │ ├── account.module.ts
│ │ │ ├── account.scss
│ │ │ └── account.ts
│ │ ├── login
│ │ │ ├── login-routing.module.ts
│ │ │ ├── login.html
│ │ │ ├── login.module.ts
│ │ │ ├── login.scss
│ │ │ └── login.ts
│ │ ├── map
│ │ │ ├── map-dark-style.js
│ │ │ ├── map-routing.module.ts
│ │ │ ├── map.html
│ │ │ ├── map.module.ts
│ │ │ ├── map.scss
│ │ │ └── map.ts
│ │ ├── schedule-filter
│ │ │ ├── schedule-filter.html
│ │ │ ├── schedule-filter.scss
│ │ │ └── schedule-filter.ts
│ │ ├── schedule
│ │ │ ├── schedule-routing.module.ts
│ │ │ ├── schedule.html
│ │ │ ├── schedule.module.ts
│ │ │ ├── schedule.scss
│ │ │ └── schedule.ts
│ │ ├── session-detail
│ │ │ ├── session-detail-routing.module.ts
│ │ │ ├── session-detail.html
│ │ │ ├── session-detail.module.ts
│ │ │ ├── session-detail.scss
│ │ │ └── session-detail.ts
│ │ ├── signup
│ │ │ ├── signup-routing.module.ts
│ │ │ ├── signup.html
│ │ │ ├── signup.module.ts
│ │ │ ├── signup.scss
│ │ │ └── signup.ts
│ │ ├── speaker-detail
│ │ │ ├── speaker-detail-routing.module.ts
│ │ │ ├── speaker-detail.html
│ │ │ ├── speaker-detail.module.ts
│ │ │ ├── speaker-detail.scss
│ │ │ └── speaker-detail.ts
│ │ ├── speaker-list
│ │ │ ├── speaker-list-routing.module.ts
│ │ │ ├── speaker-list.html
│ │ │ ├── speaker-list.module.ts
│ │ │ ├── speaker-list.scss
│ │ │ └── speaker-list.ts
│ │ ├── support
│ │ │ ├── support-routing.module.ts
│ │ │ ├── support.html
│ │ │ ├── support.module.ts
│ │ │ ├── support.scss
│ │ │ └── support.ts
│ │ ├── tabs-page
│ │ │ ├── tabs-page-routing.module.ts
│ │ │ ├── tabs-page.html
│ │ │ ├── tabs-page.module.ts
│ │ │ ├── tabs-page.scss
│ │ │ └── tabs-page.ts
│ │ └── tutorial
│ │ │ ├── tutorial-routing.module.ts
│ │ │ ├── tutorial.html
│ │ │ ├── tutorial.module.ts
│ │ │ ├── tutorial.scss
│ │ │ └── tutorial.ts
│ └── providers
│ │ ├── check-tutorial.service.ts
│ │ ├── conference-data.ts
│ │ └── user-data.ts
├── assets
│ ├── data
│ │ └── data.json
│ ├── icons
│ │ └── .gitkeep
│ └── img
│ │ ├── appicon.png
│ │ ├── appicon.svg
│ │ ├── ica-slidebox-img-1.png
│ │ ├── ica-slidebox-img-2.png
│ │ ├── ica-slidebox-img-3.png
│ │ ├── ica-slidebox-img-4.png
│ │ ├── ionic-logo-white.svg
│ │ └── speakers
│ │ ├── bear.jpg
│ │ ├── cheetah.jpg
│ │ ├── duck.jpg
│ │ ├── eagle.jpg
│ │ ├── elephant.jpg
│ │ ├── giraffe.jpg
│ │ ├── iguana.jpg
│ │ ├── kitten.jpg
│ │ ├── lion.jpg
│ │ ├── mouse.jpg
│ │ ├── puppy.jpg
│ │ ├── rabbit.jpg
│ │ └── turtle.jpg
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── global.scss
├── index.html
├── karma.conf.js
├── main.ts
├── polyfills.ts
├── test.ts
├── theme
│ └── variables.scss
└── zone-flags.ts
├── superstatic.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Specifies intentionally untracked files to ignore when using Git
2 | # http://git-scm.com/docs/gitignore
3 | www/
4 |
5 | *~
6 | *.sw[mnpcod]
7 | *.log
8 | *.tmp
9 | *.tmp.*
10 | log.txt
11 | *.sublime-project
12 | *.sublime-workspace
13 | .vscode/
14 | npm-debug.log*
15 | .firebase/
16 | .idea/
17 | .sourcemaps/
18 | .sass-cache/
19 | .tmp/
20 | .versions/
21 | coverage/
22 | dist/
23 | node_modules/
24 | tmp/
25 | temp/
26 | hooks/
27 | platforms/
28 | plugins/
29 | plugins/android.json
30 | plugins/ios.json
31 | $RECYCLE.BIN/
32 | package-lock.json
33 |
34 | .DS_Store
35 | Thumbs.db
36 | UserInterfaceState.xcuserstate
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2015 Drifty Co.
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build a production ready PWA with Angular and Firebase
2 |
3 | 
4 |
5 | Welcome to the workshop of building a production ready Progressive Web App based on Angular, Ionic and Firebase.
6 |
7 | This workshop consists of multiple steps for producing a PWA by transforming a regular web app - [Ionic Conference App](https://github.com/ionic-team/ionic-conference-app/) into a PWA, optimizing it for various platforms and powering it up with Firebase services.
8 |
9 | ## Ionic Conference App
10 |
11 | The end application is purely a kitchen-sink demo of a PWA based on Ionic Framework and Angular. We will be using some of Ionic's UI components and services with Angular for creating a uniform and native-like user experiences in multiple platforms.
12 |
13 | **We will not use Ionic for building a hybrid mobile app, but instead we will use it for building a PWA!**
14 |
15 | | Material Design | iOS |
16 | | -----------------| -----|
17 | |  |  |
18 |
19 | ## How this workshop works?
20 |
21 | Since we depend on Angular CLI and some other tools which are not available for online code editor environments, we're going to develop and build the app on our local computers.
22 |
23 | **Every next step/branch includes the solution of the previous step.**
24 |
25 | > If you're stuck at any of the steps, you can switch to the next step/branch and continue from there. Note that you need to discard your local changes on git when you checkout a solution.
26 |
27 | ## Requirements for local development environment
28 |
29 | - Google Chrome - [Download](https://www.google.com/chrome/)
30 | - node.js > 8.0.0 & npm > 5.2.0 - [Download](https://nodejs.org/en/)
31 | - Open a Firebase Account (FREE) - [Link](https://firebase.google.com/)
32 | - GIT - [Download](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
33 | - Clone this repo - Run `git clone https://github.com/onderceylan/pwa-workshop-angular-firebase`
34 | - Run `npm i`
35 |
36 | ## Table of Contents
37 |
38 | 1. [Add @angular/pwa schematic](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-1/README.md)
39 | 2. [Change web app manifest](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-2/README.md)
40 | 3. [Customize app icons and splash screens](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-3/README.md)
41 | 4. [Display A2HS on iOS](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-4/README.md)
42 | 5. [Add asset groups for app shell and icons](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-5/README.md)
43 | 6. [Add data group for conference data](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-6/README.md)
44 | 7. [Extend NGSW](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-7/README.md)
45 | 8. [Update PWA](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-8/README.md)
46 | 9. [Host on Firebase](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-9/README.md)
47 | 10. [Use an Android Emulator](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-10/README.md)
48 | 11. [Serve a secure local server](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-11/README.md)
49 | 12. [Test the A2HS functionality on Android](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-12/README.md)
50 | 13. [Add maskable icons](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-13/README.md)
51 | 14. [Subscribe to push notifications and manage permission](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-14/README.md)
52 | 15. [Send and receive push notifications](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-15/README.md)
53 | 16. [Save push subscriptions in a DB](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-16/README.md)
54 | 17. [Use an API from project Fugu](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-17/README.md)
55 | 18. [What's next?](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/final/README.md)
56 |
57 | Once you're ready with your local environment, you can start the workshop by navigation to the first step -> [Add @angular/pwa schematic](https://github.com/onderceylan/pwa-workshop-angular-firebase/blob/step-1/README.md).
58 |
59 | For questions, remarks and feedback; please contact me on [Twitter -> @onderceylan](https://twitter.com/onderceylan).
60 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "defaultProject": "app",
5 | "newProjectRoot": "projects",
6 | "projects": {
7 | "app": {
8 | "root": "",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "www",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "tsconfig.app.json",
21 | "assets": [
22 | {
23 | "glob": "**/*",
24 | "input": "src/assets",
25 | "output": "assets"
26 | },
27 | {
28 | "glob": "**/*.svg",
29 | "input": "node_modules/ionicons/dist/ionicons/svg",
30 | "output": "./svg"
31 | }
32 | ],
33 | "styles": [
34 | {
35 | "input": "src/theme/variables.scss"
36 | },
37 | {
38 | "input": "src/global.scss"
39 | }
40 | ],
41 | "scripts": []
42 | },
43 | "configurations": {
44 | "production": {
45 | "fileReplacements": [
46 | {
47 | "src": "src/environments/environment.ts",
48 | "replaceWith": "src/environments/environment.prod.ts"
49 | }
50 | ],
51 | "optimization": true,
52 | "outputHashing": "all",
53 | "sourceMap": false,
54 | "extractCss": true,
55 | "namedChunks": false,
56 | "aot": true,
57 | "extractLicenses": true,
58 | "vendorChunk": true,
59 | "buildOptimizer": true,
60 | "budgets": [
61 | {
62 | "type": "initial",
63 | "maximumWarning": "2mb",
64 | "maximumError": "5mb"
65 | }
66 | ]
67 | },
68 | "ci": {
69 | "progress": false
70 | }
71 | }
72 | },
73 | "serve": {
74 | "builder": "@angular-devkit/build-angular:dev-server",
75 | "options": {
76 | "browserTarget": "app:build"
77 | },
78 | "configurations": {
79 | "production": {
80 | "browserTarget": "app:build:production"
81 | },
82 | "ci": {
83 | "progress": false
84 | }
85 | }
86 | },
87 | "extract-i18n": {
88 | "builder": "@angular-devkit/build-angular:extract-i18n",
89 | "options": {
90 | "browserTarget": "app:build"
91 | }
92 | },
93 | "lint": {
94 | "builder": "@angular-devkit/build-angular:tslint",
95 | "options": {
96 | "tsConfig": [
97 | "tsconfig.app.json",
98 | "tsconfig.spec.json"
99 | ],
100 | "exclude": [
101 | "**/node_modules/**"
102 | ]
103 | }
104 | }
105 | }
106 | }
107 | },
108 | "cli": {
109 | "defaultCollection": "@ionic/angular-toolkit"
110 | },
111 | "schematics": {
112 | "@ionic/angular-toolkit:component": {
113 | "styleext": "scss"
114 | },
115 | "@ionic/angular-toolkit:page": {
116 | "styleext": "scss"
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
13 |
--------------------------------------------------------------------------------
/ionic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pwa-workshop-angular-firebase",
3 | "integrations": {},
4 | "type": "angular"
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pwa-workshop-angular-firebase",
3 | "version": "0.0.0",
4 | "description": "Workshop: Build a production ready PWA with Angular and Firebase",
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "ng": "ng",
8 | "start": "ng serve",
9 | "build": "ng build",
10 | "build:prod": "ng build --prod",
11 | "server": "npx superstatic www --port 8080 --host 127.0.0.1",
12 | "build:serve": "npm run build:prod",
13 | "postbuild:serve": "npm run server",
14 | "lint": "ng lint",
15 | "resources": "npx pwa-asset-generator ./src/assets/img/appicon.svg ./src/assets/pwa -b \"radial-gradient(circle farthest-corner at 1.3% 2.8%, rgba(239,249,249,1) 0%, rgba(182,199,226,1) 100%)\" -i ./src/index.html -m ./src/manifest.webmanifest -e false"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/onderceylan/pwa-workshop-angular-firebase.git"
20 | },
21 | "private": true,
22 | "dependencies": {
23 | "@angular/common": "10.1.2",
24 | "@angular/core": "10.1.2",
25 | "@angular/forms": "10.1.2",
26 | "@angular/platform-browser": "10.1.2",
27 | "@angular/platform-browser-dynamic": "10.1.2",
28 | "@angular/router": "10.1.2",
29 | "@ionic/angular": "5.3.3",
30 | "@ionic/storage": "2.3.1",
31 | "core-js": "3.4.1",
32 | "rxjs": "6.5.5",
33 | "tslib": "2.0.0",
34 | "zone.js": "0.10.3"
35 | },
36 | "devDependencies": {
37 | "@angular-devkit/build-angular": "0.1001.2",
38 | "@angular/cli": "10.1.2",
39 | "@angular/compiler": "10.1.2",
40 | "@angular/compiler-cli": "10.1.2",
41 | "@angular/language-service": "10.1.2",
42 | "@ionic/angular-toolkit": "2.3.3",
43 | "@types/node": "12.11.1",
44 | "codelyzer": "6.0.0",
45 | "firebase-tools": "8.10.0",
46 | "pwa-asset-generator": "3.2.2",
47 | "superstatic": "7.0.0",
48 | "ts-node": "8.5.4",
49 | "tslint": "6.1.3",
50 | "typescript": "3.9.5"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 | import { CheckTutorial } from './providers/check-tutorial.service';
4 |
5 | const routes: Routes = [
6 | {
7 | path: '',
8 | redirectTo: '/app/tabs/schedule',
9 | pathMatch: 'full'
10 | },
11 | {
12 | path: 'account',
13 | loadChildren: () => import('./pages/account/account.module').then(m => m.AccountModule)
14 | },
15 | {
16 | path: 'support',
17 | loadChildren: () => import('./pages/support/support.module').then(m => m.SupportModule)
18 | },
19 | {
20 | path: 'login',
21 | loadChildren: () => import('./pages/login/login.module').then(m => m.LoginModule)
22 | },
23 | {
24 | path: 'signup',
25 | loadChildren: () => import('./pages/signup/signup.module').then(m => m.SignUpModule)
26 | },
27 | {
28 | path: 'app',
29 | loadChildren: () => import('./pages/tabs-page/tabs-page.module').then(m => m.TabsModule)
30 | },
31 | {
32 | path: 'tutorial',
33 | loadChildren: () => import('./pages/tutorial/tutorial.module').then(m => m.TutorialModule),
34 | canLoad: [CheckTutorial]
35 | }
36 | ];
37 |
38 | @NgModule({
39 | imports: [RouterModule.forRoot(routes)],
40 | exports: [RouterModule]
41 | })
42 | export class AppRoutingModule {}
43 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Menu
8 |
9 |
10 |
11 |
12 |
13 |
14 | Navigate
15 |
16 |
17 |
18 |
19 |
20 | {{p.title}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Account
30 |
31 |
32 |
33 |
34 |
35 |
36 | Account
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Support
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Logout
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Account
64 |
65 |
66 |
67 |
68 |
69 |
70 | Login
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Support
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | Signup
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | Tutorial
97 |
98 |
99 |
100 |
101 | Show Tutorial
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | Dark Theme
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | ion-item {
2 | --transition: unset;
3 | }
4 |
5 | .active {
6 | --color: var(--ion-color-primary);
7 | --background: rgba(var(--ion-color-primary-rgb), 0.12);
8 | --background-hover: rgba(var(--ion-color-primary-rgb), 0.16);
9 | --background-focused: rgba(var(--ion-color-primary-rgb), 0.24);
10 |
11 | ion-icon {
12 | color: inherit;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewEncapsulation } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { MenuController } from '@ionic/angular';
4 | import { Storage } from '@ionic/storage';
5 | import { UserData } from './providers/user-data';
6 |
7 | @Component({
8 | selector: 'app-root',
9 | templateUrl: './app.component.html',
10 | styleUrls: ['./app.component.scss'],
11 | encapsulation: ViewEncapsulation.None
12 | })
13 | export class AppComponent implements OnInit {
14 | appPages = [
15 | {
16 | title: 'Schedule',
17 | url: '/app/tabs/schedule',
18 | icon: 'calendar'
19 | },
20 | {
21 | title: 'Speakers',
22 | url: '/app/tabs/speakers',
23 | icon: 'people'
24 | },
25 | {
26 | title: 'Map',
27 | url: '/app/tabs/map',
28 | icon: 'map'
29 | },
30 | {
31 | title: 'About',
32 | url: '/app/tabs/about',
33 | icon: 'information-circle'
34 | }
35 | ];
36 | loggedIn = false;
37 | dark = false;
38 |
39 | constructor(
40 | private menu: MenuController,
41 | private router: Router,
42 | private storage: Storage,
43 | private userData: UserData,
44 | ) { }
45 |
46 | async ngOnInit() {
47 | this.checkLoginStatus();
48 | this.listenForLoginEvents();
49 | }
50 |
51 | checkLoginStatus() {
52 | return this.userData.isLoggedIn().then(loggedIn => {
53 | return this.updateLoggedInStatus(loggedIn);
54 | });
55 | }
56 |
57 | updateLoggedInStatus(loggedIn: boolean) {
58 | setTimeout(() => {
59 | this.loggedIn = loggedIn;
60 | }, 300);
61 | }
62 |
63 | listenForLoginEvents() {
64 | window.addEventListener('user:login', () => {
65 | this.updateLoggedInStatus(true);
66 | });
67 |
68 | window.addEventListener('user:signup', () => {
69 | this.updateLoggedInStatus(true);
70 | });
71 |
72 | window.addEventListener('user:logout', () => {
73 | this.updateLoggedInStatus(false);
74 | });
75 | }
76 |
77 | logout() {
78 | this.userData.logout().then(() => {
79 | return this.router.navigateByUrl('/app/tabs/schedule');
80 | });
81 | }
82 |
83 | openTutorial() {
84 | this.menu.enable(false);
85 | this.storage.set('ion_did_tutorial', false);
86 | this.router.navigateByUrl('/tutorial');
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientModule } from '@angular/common/http';
2 | import { NgModule } from '@angular/core';
3 | import { BrowserModule } from '@angular/platform-browser';
4 | import { FormsModule } from '@angular/forms';
5 | import { IonicModule } from '@ionic/angular';
6 | import { IonicStorageModule } from '@ionic/storage';
7 | import { AppRoutingModule } from './app-routing.module';
8 | import { AppComponent } from './app.component';
9 |
10 | @NgModule({
11 | imports: [
12 | BrowserModule,
13 | AppRoutingModule,
14 | HttpClientModule,
15 | FormsModule,
16 | IonicModule.forRoot(),
17 | IonicStorageModule.forRoot(),
18 | ],
19 | declarations: [AppComponent],
20 | bootstrap: [AppComponent]
21 | })
22 | export class AppModule {}
23 |
--------------------------------------------------------------------------------
/src/app/app.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/app/app.scss
--------------------------------------------------------------------------------
/src/app/interfaces/user-options.ts:
--------------------------------------------------------------------------------
1 | export interface UserOptions {
2 | username: string;
3 | password: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/pages/about-popover/about-popover.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { PopoverController } from '@ionic/angular';
3 |
4 | @Component({
5 | template: `
6 |
7 |
8 | Learn Ionic
9 |
10 |
11 | Documentation
12 |
13 |
14 | Showcase
15 |
16 |
17 | GitHub Repo
18 |
19 |
20 | Support
21 |
22 |
23 | `
24 | })
25 | export class PopoverPage {
26 | constructor(public popoverCtrl: PopoverController) {}
27 |
28 | support() {
29 | // this.app.getRootNavs()[0].push('/support');
30 | this.popoverCtrl.dismiss();
31 | }
32 |
33 | close(url: string) {
34 | window.open(url, '_blank');
35 | this.popoverCtrl.dismiss();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/pages/about/about-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 | import { AboutPage } from './about';
4 |
5 | const routes: Routes = [
6 | {
7 | path: '',
8 | component: AboutPage
9 | }
10 | ];
11 |
12 | @NgModule({
13 | imports: [RouterModule.forChild(routes)],
14 | exports: [RouterModule]
15 | })
16 | export class AboutPageRoutingModule { }
17 |
--------------------------------------------------------------------------------
/src/app/pages/about/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
42 | The Ionic Conference is a one-day conference featuring talks from the Ionic team. It is focused on Ionic applications being
43 | built with Ionic Framework. This includes migrating apps to the latest Ionic Framework, Angular concepts, Webpack, Sass, and many
44 | other technologies used in Ionic Framework. Tickets are completely sold out, and we’re expecting more than 1000 developers
45 | – making this the largest Ionic conference ever!
46 |
18 | The
19 | ionic conference app is a practical preview of the ionic framework in action, and a demonstration of proper code
20 | use.
21 |
22 |
23 |
24 |
25 |
26 |
What is Ionic?
27 |
28 | Ionic Framework is an open source SDK that enables developers to build high quality mobile apps with web technologies
29 | like HTML, CSS, and JavaScript.
30 |
31 |
32 |
33 |
34 |
What is Ionic Appflow?
35 |
36 | Ionic Appflow is a powerful set of services and features built on top of Ionic Framework that brings a totally new
37 | level of app development agility to mobile dev teams.
38 |
39 |
40 |
41 |
42 |
Ready to Play?
43 |
44 | Continue
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/app/pages/tutorial/tutorial.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { IonicModule } from '@ionic/angular';
4 | import { TutorialPage } from './tutorial';
5 | import { TutorialPageRoutingModule } from './tutorial-routing.module';
6 |
7 | @NgModule({
8 | imports: [
9 | CommonModule,
10 | IonicModule,
11 | TutorialPageRoutingModule
12 | ],
13 | declarations: [TutorialPage],
14 | entryComponents: [TutorialPage],
15 | })
16 | export class TutorialModule {}
17 |
--------------------------------------------------------------------------------
/src/app/pages/tutorial/tutorial.scss:
--------------------------------------------------------------------------------
1 | ion-toolbar {
2 | // TODO test transparent and fullscreen
3 | --background: transparent;
4 | --border-color: transparent;
5 | }
6 |
7 | .swiper-slide {
8 | display: block;
9 | }
10 |
11 | .slide-title {
12 | margin-top: 2.8rem;
13 | }
14 |
15 | .slide-image {
16 | max-height: 50%;
17 | max-width: 60%;
18 | margin: 36px 0;
19 | pointer-events: none;
20 | }
21 |
22 | b {
23 | font-weight: 500;
24 | }
25 |
26 | p {
27 | padding: 0 40px;
28 | font-size: 14px;
29 | line-height: 1.5;
30 | color: var(--ion-color-step-600, #60646b);
31 |
32 | b {
33 | color: var(--ion-text-color, #000000);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/pages/tutorial/tutorial.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewChild } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { MenuController, IonSlides } from '@ionic/angular';
4 | import { Storage } from '@ionic/storage';
5 |
6 | @Component({
7 | selector: 'page-tutorial',
8 | templateUrl: 'tutorial.html',
9 | styleUrls: ['./tutorial.scss'],
10 | })
11 | export class TutorialPage {
12 | showSkip = true;
13 |
14 | @ViewChild('slides', { static: true }) slides: IonSlides;
15 |
16 | constructor(
17 | public menu: MenuController,
18 | public router: Router,
19 | public storage: Storage
20 | ) {}
21 |
22 | startApp() {
23 | this.router
24 | .navigateByUrl('/app/tabs/schedule', { replaceUrl: true })
25 | .then(() => this.storage.set('ion_did_tutorial', true));
26 | }
27 |
28 | onSlideChangeStart(event) {
29 | event.target.isEnd().then(isEnd => {
30 | this.showSkip = !isEnd;
31 | });
32 | }
33 |
34 | ionViewWillEnter() {
35 | this.storage.get('ion_did_tutorial').then(res => {
36 | if (res === true) {
37 | this.router.navigateByUrl('/app/tabs/schedule', { replaceUrl: true });
38 | }
39 | });
40 |
41 | this.menu.enable(false);
42 | }
43 |
44 | ionViewDidLeave() {
45 | // enable the root left menu when leaving the tutorial page
46 | this.menu.enable(true);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/providers/check-tutorial.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { CanLoad, Router } from '@angular/router';
3 | import { Storage } from '@ionic/storage';
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class CheckTutorial implements CanLoad {
8 | constructor(private storage: Storage, private router: Router) {}
9 |
10 | canLoad() {
11 | return this.storage.get('ion_did_tutorial').then(res => {
12 | if (res) {
13 | this.router.navigate(['/app', 'tabs', 'schedule']);
14 | return false;
15 | } else {
16 | return true;
17 | }
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/providers/conference-data.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { of } from 'rxjs';
4 | import { map } from 'rxjs/operators';
5 | import { UserData } from './user-data';
6 |
7 | @Injectable({
8 | providedIn: 'root'
9 | })
10 | export class ConferenceData {
11 | data: any;
12 |
13 | constructor(public http: HttpClient, public user: UserData) {}
14 |
15 | load(): any {
16 | if (this.data) {
17 | return of(this.data);
18 | } else {
19 | return this.http
20 | .get('assets/data/data.json')
21 | .pipe(map(this.processData, this));
22 | }
23 | }
24 |
25 | processData(data: any) {
26 | // just some good 'ol JS fun with objects and arrays
27 | // build up the data by linking speakers to sessions
28 | this.data = data;
29 |
30 | // loop through each day in the schedule
31 | this.data.schedule.forEach((day: any) => {
32 | // loop through each timeline group in the day
33 | day.groups.forEach((group: any) => {
34 | // loop through each session in the timeline group
35 | group.sessions.forEach((session: any) => {
36 | session.speakers = [];
37 | if (session.speakerNames) {
38 | session.speakerNames.forEach((speakerName: any) => {
39 | const speaker = this.data.speakers.find(
40 | (s: any) => s.name === speakerName
41 | );
42 | if (speaker) {
43 | session.speakers.push(speaker);
44 | speaker.sessions = speaker.sessions || [];
45 | speaker.sessions.push(session);
46 | }
47 | });
48 | }
49 | });
50 | });
51 | });
52 |
53 | return this.data;
54 | }
55 |
56 | getTimeline(
57 | dayIndex: number,
58 | queryText = '',
59 | excludeTracks: any[] = [],
60 | segment = 'all'
61 | ) {
62 | return this.load().pipe(
63 | map((data: any) => {
64 | const day = data.schedule[dayIndex];
65 | day.shownSessions = 0;
66 |
67 | queryText = queryText.toLowerCase().replace(/,|\.|-/g, ' ');
68 | const queryWords = queryText.split(' ').filter(w => !!w.trim().length);
69 |
70 | day.groups.forEach((group: any) => {
71 | group.hide = true;
72 |
73 | group.sessions.forEach((session: any) => {
74 | // check if this session should show or not
75 | this.filterSession(session, queryWords, excludeTracks, segment);
76 |
77 | if (!session.hide) {
78 | // if this session is not hidden then this group should show
79 | group.hide = false;
80 | day.shownSessions++;
81 | }
82 | });
83 | });
84 |
85 | return day;
86 | })
87 | );
88 | }
89 |
90 | filterSession(
91 | session: any,
92 | queryWords: string[],
93 | excludeTracks: any[],
94 | segment: string
95 | ) {
96 | let matchesQueryText = false;
97 | if (queryWords.length) {
98 | // of any query word is in the session name than it passes the query test
99 | queryWords.forEach((queryWord: string) => {
100 | if (session.name.toLowerCase().indexOf(queryWord) > -1) {
101 | matchesQueryText = true;
102 | }
103 | });
104 | } else {
105 | // if there are no query words then this session passes the query test
106 | matchesQueryText = true;
107 | }
108 |
109 | // if any of the sessions tracks are not in the
110 | // exclude tracks then this session passes the track test
111 | let matchesTracks = false;
112 | session.tracks.forEach((trackName: string) => {
113 | if (excludeTracks.indexOf(trackName) === -1) {
114 | matchesTracks = true;
115 | }
116 | });
117 |
118 | // if the segment is 'favorites', but session is not a user favorite
119 | // then this session does not pass the segment test
120 | let matchesSegment = false;
121 | if (segment === 'favorites') {
122 | if (this.user.hasFavorite(session.name)) {
123 | matchesSegment = true;
124 | }
125 | } else {
126 | matchesSegment = true;
127 | }
128 |
129 | // all tests must be true if it should not be hidden
130 | session.hide = !(matchesQueryText && matchesTracks && matchesSegment);
131 | }
132 |
133 | getSpeakers() {
134 | return this.load().pipe(
135 | map((data: any) => {
136 | return data.speakers.sort((a: any, b: any) => {
137 | const aName = a.name.split(' ').pop();
138 | const bName = b.name.split(' ').pop();
139 | return aName.localeCompare(bName);
140 | });
141 | })
142 | );
143 | }
144 |
145 | getTracks() {
146 | return this.load().pipe(
147 | map((data: any) => {
148 | return data.tracks.sort();
149 | })
150 | );
151 | }
152 |
153 | getMap() {
154 | return this.load().pipe(
155 | map((data: any) => {
156 | return data.map;
157 | })
158 | );
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/app/providers/user-data.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Storage } from '@ionic/storage';
3 |
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class UserData {
8 | _favorites: string[] = [];
9 | HAS_LOGGED_IN = 'hasLoggedIn';
10 | HAS_SEEN_TUTORIAL = 'hasSeenTutorial';
11 |
12 | constructor(
13 | public storage: Storage
14 | ) { }
15 |
16 | hasFavorite(sessionName: string): boolean {
17 | return (this._favorites.indexOf(sessionName) > -1);
18 | }
19 |
20 | addFavorite(sessionName: string): void {
21 | this._favorites.push(sessionName);
22 | }
23 |
24 | removeFavorite(sessionName: string): void {
25 | const index = this._favorites.indexOf(sessionName);
26 | if (index > -1) {
27 | this._favorites.splice(index, 1);
28 | }
29 | }
30 |
31 | login(username: string): Promise {
32 | return this.storage.set(this.HAS_LOGGED_IN, true).then(() => {
33 | this.setUsername(username);
34 | return window.dispatchEvent(new CustomEvent('user:login'));
35 | });
36 | }
37 |
38 | signup(username: string): Promise {
39 | return this.storage.set(this.HAS_LOGGED_IN, true).then(() => {
40 | this.setUsername(username);
41 | return window.dispatchEvent(new CustomEvent('user:signup'));
42 | });
43 | }
44 |
45 | logout(): Promise {
46 | return this.storage.remove(this.HAS_LOGGED_IN).then(() => {
47 | return this.storage.remove('username');
48 | }).then(() => {
49 | window.dispatchEvent(new CustomEvent('user:logout'));
50 | });
51 | }
52 |
53 | setUsername(username: string): Promise {
54 | return this.storage.set('username', username);
55 | }
56 |
57 | getUsername(): Promise {
58 | return this.storage.get('username').then((value) => {
59 | return value;
60 | });
61 | }
62 |
63 | isLoggedIn(): Promise {
64 | return this.storage.get(this.HAS_LOGGED_IN).then((value) => {
65 | return value === true;
66 | });
67 | }
68 |
69 | checkHasSeenTutorial(): Promise {
70 | return this.storage.get(this.HAS_SEEN_TUTORIAL).then((value) => {
71 | return value;
72 | });
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/assets/data/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "schedule": [
3 | {
4 | "date": "2047-05-17",
5 | "groups": [
6 | {
7 | "time": "8:00 am",
8 | "sessions": [
9 | {
10 | "name": "Breakfast",
11 | "timeStart": "8:00 am",
12 | "timeEnd": "9:00 am",
13 | "location": "Dining Hall",
14 | "tracks": ["Food"],
15 | "id": "1"
16 | }
17 | ]
18 | },
19 | {
20 | "time": "9:15 am",
21 | "sessions": [
22 | {
23 | "name": "Getting Started with Ionic",
24 | "location": "Hall 2",
25 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
26 | "speakerNames": ["Ted Turtle"],
27 | "timeStart": "9:30 am",
28 | "timeEnd": "9:45 am",
29 | "tracks": ["Ionic"],
30 | "id": "2"
31 | },
32 | {
33 | "name": "Ionic Tooling",
34 | "location": "Executive Ballroom",
35 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
36 | "speakerNames": ["Rachel Rabbit"],
37 | "timeStart": "9:45 am",
38 | "timeEnd": "10:00 am",
39 | "tracks": ["Tooling"],
40 | "id": "3"
41 | },
42 | {
43 | "name": "University of Ionic",
44 | "location": "Hall 3",
45 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
46 | "speakerNames": ["Ellie Elephant"],
47 | "timeStart": "9:15 am",
48 | "timeEnd": "9:30 am",
49 | "tracks": ["Ionic"],
50 | "id": "4"
51 | }
52 | ]
53 | },
54 | {
55 | "time": "10:00 am",
56 | "sessions": [
57 | {
58 | "name": "Migrating to Ionic",
59 | "location": "Hall 1",
60 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
61 | "speakerNames": ["Eva Eagle", "Lionel Lion"],
62 | "timeStart": "10:00 am",
63 | "timeEnd": "10:15 am",
64 | "tracks": ["Ionic"],
65 | "id": "5"
66 | },
67 | {
68 | "name": "What's New in Angular",
69 | "location": "Hall 3",
70 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
71 | "speakerNames": ["Rachel Rabbit"],
72 | "timeStart": "10:15 am",
73 | "timeEnd": "10:30 am",
74 | "tracks": ["Angular"],
75 | "id": "6"
76 | },
77 | {
78 | "name": "The Evolution of Ionicons",
79 | "location": "Hall 2",
80 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
81 | "speakerNames": ["Isabella Iguana", "Eva Eagle"],
82 | "timeStart": "10:15 am",
83 | "timeEnd": "10:30 am",
84 | "tracks": ["Design"],
85 | "id": "7"
86 | },
87 | {
88 | "name": "Ionic Pro",
89 | "location": "Grand Ballroom A",
90 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
91 | "speakerNames": ["Charlie Cheetah"],
92 | "timeStart": "10:45 am",
93 | "timeEnd": "11:00 am",
94 | "tracks": ["Services"],
95 | "id": "8"
96 | }
97 | ]
98 | },
99 | {
100 | "time": "11:00 am",
101 | "sessions": [
102 | {
103 | "name": "Ionic Workshop",
104 | "location": "Hall 1",
105 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
106 | "speakerNames": ["Karl Kitten", "Lionel Lion"],
107 | "timeStart": "11:00 am",
108 | "timeEnd": "11:45 am",
109 | "tracks": ["Workshop"],
110 | "id": "9"
111 | },
112 | {
113 | "name": "Community Interaction",
114 | "location": "Hall 3",
115 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
116 | "speakerNames": ["Lionel Lion", "Gino Giraffe"],
117 | "timeStart": "11:30 am",
118 | "timeEnd": "11:50 am",
119 | "tracks": ["Communication"],
120 | "id": "10"
121 | },
122 | {
123 | "name": "Navigation in Ionic",
124 | "location": "Grand Ballroom A",
125 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
126 | "speakerNames": ["Rachel Rabbit", "Eva Eagle"],
127 | "timeStart": "11:30 am",
128 | "timeEnd": "12:00 pm",
129 | "tracks": ["Navigation"],
130 | "id": "11"
131 | }
132 | ]
133 | },
134 | {
135 | "time": "12:00 pm",
136 | "sessions": [
137 | {
138 | "name": "Lunch",
139 | "location": "Dining Hall",
140 | "description": "Come grab lunch with all the Ionic fanatics and talk all things Ionic",
141 | "timeStart": "12:00 pm",
142 | "timeEnd": "1:00 pm",
143 | "tracks": ["Food"],
144 | "id": "12"
145 | }
146 | ]
147 | },
148 | {
149 | "time": "1:00 pm",
150 | "sessions": [
151 | {
152 | "name": "Ionic in the Enterprise",
153 | "location": "Hall 1",
154 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
155 | "speakerNames": ["Paul Puppy"],
156 | "timeStart": "1:00 pm",
157 | "timeEnd": "1:15 pm",
158 | "tracks": ["Communication"],
159 | "id": "13"
160 | },
161 | {
162 | "name": "Ionic Worldwide",
163 | "location": "Hall 1",
164 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
165 | "speakerNames": ["Gino Giraffe"],
166 | "timeStart": "1:15 pm",
167 | "timeEnd": "1:30 pm",
168 | "tracks": ["Communication"],
169 | "id": "14"
170 | },
171 | {
172 | "name": "The Ionic Package",
173 | "location": "Grand Ballroom B",
174 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
175 | "speakerNames": ["Molly Mouse", "Burt Bear"],
176 | "timeStart": "1:30 pm",
177 | "timeEnd": "2:00 pm",
178 | "tracks": ["Services"],
179 | "id": "15"
180 | }
181 | ]
182 | },
183 | {
184 | "time": "2:00 pm",
185 | "sessions": [
186 | {
187 | "name": "Push Notifications in Ionic",
188 | "location": "Hall 2",
189 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
190 | "speakerNames": ["Burt Bear", "Charlie Cheetah"],
191 | "timeStart": "2:00 pm",
192 | "timeEnd": "2:30 pm",
193 | "tracks": ["Services"],
194 | "id": "16"
195 | },
196 | {
197 | "name": "Ionic Documentation",
198 | "location": "Grand Ballroom B",
199 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
200 | "speakerNames": ["Donald Duck"],
201 | "timeStart": "2:30 pm",
202 | "timeEnd": "2:45 pm",
203 | "tracks": ["Documentation"],
204 | "id": "17"
205 | },
206 | {
207 | "name": "UX in Ionic",
208 | "location": "Hall 3",
209 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
210 | "speakerNames": ["Isabella Iguana", "Ellie Elephant"],
211 | "timeStart": "2:45 pm",
212 | "timeEnd": "3:00 pm",
213 | "tracks": ["Design"],
214 | "id": "18"
215 | }
216 | ]
217 | },
218 | {
219 | "time": "3:00",
220 | "sessions": [
221 | {
222 | "name": "Angular Directives in Ionic",
223 | "location": "Hall 1",
224 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
225 | "speakerNames": ["Ted Turtle"],
226 | "timeStart": "3:00 pm",
227 | "timeEnd": "3:30 pm",
228 | "tracks": ["Angular"],
229 | "id": "19"
230 | },
231 | {
232 | "name": "Mobile States",
233 | "location": "Hall 2",
234 | "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
235 | "speakerNames": ["Rachel Rabbit"],
236 | "timeStart": "3:30 pm",
237 | "timeEnd": "3:45 pm",
238 | "tracks": ["Navigation"],
239 | "id": "20"
240 | }
241 | ]
242 | }
243 | ]
244 | }
245 | ],
246 |
247 | "speakers": [
248 | {
249 | "name": "Burt Bear",
250 | "profilePic": "assets/img/speakers/bear.jpg",
251 | "instagram": "ionicframework",
252 | "twitter": "ionicframework",
253 | "github": "ionic-team/ionic",
254 | "about": "Burt is a Bear.",
255 | "location": "Everywhere",
256 | "email": "burt@example.com",
257 | "phone": "+1-541-754-3010",
258 | "id": "1"
259 | },
260 | {
261 | "name": "Charlie Cheetah",
262 | "profilePic": "assets/img/speakers/cheetah.jpg",
263 | "instagram": "ionicframework",
264 | "twitter": "ionicframework",
265 | "github": "ionic-team/ionic",
266 | "about": "Charlie is a Cheetah.",
267 | "location": "Everywhere",
268 | "email": "charlie@example.com",
269 | "phone": "+1-541-754-3010",
270 | "id": "2"
271 | },
272 | {
273 | "name": "Donald Duck",
274 | "profilePic": "assets/img/speakers/duck.jpg",
275 | "instagram": "ionicframework",
276 | "twitter": "ionicframework",
277 | "github": "ionic-team/ionic",
278 | "about": "Donald is a Duck.",
279 | "location": "Everywhere",
280 | "email": "donald@example.com",
281 | "phone": "+1-541-754-3010",
282 | "id": "3"
283 | },
284 | {
285 | "name": "Eva Eagle",
286 | "profilePic": "assets/img/speakers/eagle.jpg",
287 | "instagram": "ionicframework",
288 | "twitter": "ionicframework",
289 | "github": "ionic-team/ionic",
290 | "about": "Eva is an Eagle.",
291 | "location": "Everywhere",
292 | "email": "eva@example.com",
293 | "phone": "+1-541-754-3010",
294 | "id": "4"
295 | },
296 | {
297 | "name": "Ellie Elephant",
298 | "profilePic": "assets/img/speakers/elephant.jpg",
299 | "instagram": "ionicframework",
300 | "twitter": "ionicframework",
301 | "github": "ionic-team/ionic",
302 | "about": "Ellie is an Elephant.",
303 | "location": "Everywhere",
304 | "email": "ellie@example.com",
305 | "phone": "+1-541-754-3010",
306 | "id": "5"
307 | },
308 | {
309 | "name": "Gino Giraffe",
310 | "profilePic": "assets/img/speakers/giraffe.jpg",
311 | "instagram": "ionicframework",
312 | "twitter": "ionicframework",
313 | "github": "ionic-team/ionic",
314 | "about": "Gino is a Giraffe.",
315 | "location": "Everywhere",
316 | "email": "gino@example.com",
317 | "phone": "+1-541-754-3010",
318 | "id": "6"
319 | },
320 | {
321 | "name": "Isabella Iguana",
322 | "profilePic": "assets/img/speakers/iguana.jpg",
323 | "instagram": "ionicframework",
324 | "twitter": "ionicframework",
325 | "github": "ionic-team/ionic",
326 | "about": "Isabella is an Iguana.",
327 | "location": "Everywhere",
328 | "email": "isabella@example.com",
329 | "phone": "+1-541-754-3010",
330 | "id": "7"
331 | },
332 | {
333 | "name": "Karl Kitten",
334 | "profilePic": "assets/img/speakers/kitten.jpg",
335 | "instagram": "ionicframework",
336 | "twitter": "ionicframework",
337 | "github": "ionic-team/ionic",
338 | "about": "Karl is a Kitten.",
339 | "location": "Everywhere",
340 | "email": "karl@example.com",
341 | "phone": "+1-541-754-3010",
342 | "id": "8"
343 | },
344 | {
345 | "name": "Lionel Lion",
346 | "profilePic": "assets/img/speakers/lion.jpg",
347 | "instagram": "ionicframework",
348 | "twitter": "ionicframework",
349 | "github": "ionic-team/ionic",
350 | "about": "Lionel is a Lion.",
351 | "location": "Everywhere",
352 | "email": "lionel@example.com",
353 | "phone": "+1-541-754-3010",
354 | "id": "9"
355 | },
356 | {
357 | "name": "Molly Mouse",
358 | "profilePic": "assets/img/speakers/mouse.jpg",
359 | "instagram": "ionicframework",
360 | "twitter": "ionicframework",
361 | "github": "ionic-team/ionic",
362 | "about": "Molly is a Mouse.",
363 | "location": "Everywhere",
364 | "email": "molly@example.com",
365 | "phone": "+1-541-754-3010",
366 | "id": "10"
367 | },
368 | {
369 | "name": "Paul Puppy",
370 | "profilePic": "assets/img/speakers/puppy.jpg",
371 | "instagram": "ionicframework",
372 | "twitter": "ionicframework",
373 | "github": "ionic-team/ionic",
374 | "about": "Paul is a Puppy.",
375 | "location": "Everywhere",
376 | "email": "paul@example.com",
377 | "phone": "+1-541-754-3010",
378 | "id": "11"
379 | },
380 | {
381 | "name": "Rachel Rabbit",
382 | "profilePic": "assets/img/speakers/rabbit.jpg",
383 | "instagram": "ionicframework",
384 | "twitter": "ionicframework",
385 | "github": "ionic-team/ionic",
386 | "about": "Rachel is a Rabbit.",
387 | "location": "Everywhere",
388 | "email": "rachel@example.com",
389 | "phone": "+1-541-754-3010",
390 | "id": "12"
391 | },
392 | {
393 | "name": "Ted Turtle",
394 | "profilePic": "assets/img/speakers/turtle.jpg",
395 | "instagram": "ionicframework",
396 | "twitter": "ionicframework",
397 | "github": "ionic-team/ionic",
398 | "about": "Ted is a Turtle.",
399 | "location": "Everywhere",
400 | "email": "ted@example.com",
401 | "phone": "+1-541-754-3010",
402 | "id": "13"
403 | }
404 | ],
405 |
406 | "map": [
407 | {
408 | "name": "Monona Terrace Convention Center",
409 | "lat": 43.071584,
410 | "lng": -89.38012,
411 | "center": true
412 | },
413 | {
414 | "name": "Ionic HQ",
415 | "lat": 43.074395,
416 | "lng": -89.381056
417 | },
418 | {
419 | "name": "Afterparty - Brocach Irish Pub",
420 | "lat": 43.07336,
421 | "lng": -89.38335
422 | }
423 | ],
424 |
425 | "tracks": [
426 | {
427 | "name": "Angular",
428 | "icon": "logo-angular"
429 | },
430 | {
431 | "name": "Documentation",
432 | "icon": "document"
433 | },
434 | {
435 | "name": "Food",
436 | "icon": "restaurant"
437 | },
438 | {
439 | "name": "Ionic",
440 | "icon": "logo-ionic"
441 | },
442 | {
443 | "name": "Tooling",
444 | "icon": "hammer"
445 | },
446 | {
447 | "name": "Design",
448 | "icon": "color-palette"
449 | },
450 | {
451 | "name": "Services",
452 | "icon": "cog"
453 | },
454 | {
455 | "name": "Workshop",
456 | "icon": "construct"
457 | },
458 | {
459 | "name": "Communication",
460 | "icon": "call"
461 | },
462 | {
463 | "name": "Navigation",
464 | "icon": "compass"
465 | }
466 | ]
467 | }
468 |
--------------------------------------------------------------------------------
/src/assets/icons/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/icons/.gitkeep
--------------------------------------------------------------------------------
/src/assets/img/appicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/appicon.png
--------------------------------------------------------------------------------
/src/assets/img/appicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/img/ica-slidebox-img-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/ica-slidebox-img-1.png
--------------------------------------------------------------------------------
/src/assets/img/ica-slidebox-img-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/ica-slidebox-img-2.png
--------------------------------------------------------------------------------
/src/assets/img/ica-slidebox-img-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/ica-slidebox-img-3.png
--------------------------------------------------------------------------------
/src/assets/img/ica-slidebox-img-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/ica-slidebox-img-4.png
--------------------------------------------------------------------------------
/src/assets/img/ionic-logo-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/img/speakers/bear.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/bear.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/cheetah.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/cheetah.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/duck.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/duck.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/eagle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/eagle.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/elephant.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/elephant.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/giraffe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/giraffe.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/iguana.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/iguana.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/kitten.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/kitten.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/lion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/lion.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/mouse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/mouse.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/puppy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/puppy.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/rabbit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/rabbit.jpg
--------------------------------------------------------------------------------
/src/assets/img/speakers/turtle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elegantapp/pwa-workshop-angular-firebase/b98d3d9d591d15703c9a012175832ed26d2be540/src/assets/img/speakers/turtle.jpg
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * In development mode, to ignore zone related error stack frames such as
11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
12 | * import the following file, but please comment it out in production mode
13 | * because it will have performance impact when throw error
14 | */
15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
16 |
--------------------------------------------------------------------------------
/src/global.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * App Global CSS
3 | * ----------------------------------------------------------------------------
4 | * Put style rules here that you want to apply globally. These styles are for
5 | * the entire app and not just one component. Additionally, this file can be
6 | * used as an entry point to import other CSS/Sass files to be included in the
7 | * output CSS.
8 | * For more information on global stylesheets, visit the documentation:
9 | * https://ionicframework.com/docs/layout/global-stylesheets
10 | */
11 |
12 | /* Core CSS required for Ionic components to work properly */
13 | @import "~@ionic/angular/css/core.css";
14 |
15 | /* Basic CSS for apps built with Ionic */
16 | @import "~@ionic/angular/css/normalize.css";
17 | @import "~@ionic/angular/css/structure.css";
18 | @import "~@ionic/angular/css/typography.css";
19 |
20 | /* Optional CSS utils that can be commented out */
21 | @import "~@ionic/angular/css/padding.css";
22 | @import "~@ionic/angular/css/float-elements.css";
23 | @import "~@ionic/angular/css/text-alignment.css";
24 | @import "~@ionic/angular/css/text-transformation.css";
25 | @import "~@ionic/angular/css/flex-utils.css";
26 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Ionic Conference PWA
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, 'coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags.ts';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | import './zone-flags';
56 |
57 | /***************************************************************************************************
58 | * Zone JS is required by default for Angular itself.
59 | */
60 |
61 | import 'zone.js/dist/zone'; // Included with Angular CLI.
62 |
63 |
64 | /***************************************************************************************************
65 | * APPLICATION IMPORTS
66 | */
67 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/src/theme/variables.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Ionic Variables and Theming
3 | * ----------------------------------------------------------------------------
4 | * For more information, please see
5 | * https://www.ionicframework.com/docs/theming/
6 | */
7 |
8 | /*
9 | * Ionic Colors
10 | * ----------------------------------------------------------------------------
11 | * Named colors make it easy to reuse colors on various components.
12 | * It's highly recommended to change the default colors
13 | * to match your app's branding. Ionic provides nine layered colors
14 | * that can be changed to theme an app. Additional colors can be
15 | * added as well (see below). For more information, please see
16 | * https://www.ionicframework.com/docs/theming/colors
17 | *
18 | * To easily create custom color palettes for your app’s UI,
19 | * check out our color generator:
20 | * https://www.ionicframework.com/docs/theming/color-generator
21 | */
22 |
23 | :root {
24 | --ion-color-primary: #3880ff;
25 | --ion-color-primary-rgb: 56, 128, 255;
26 | --ion-color-primary-contrast: #ffffff;
27 | --ion-color-primary-contrast-rgb: 255, 255, 255;
28 | --ion-color-primary-shade: #3171e0;
29 | --ion-color-primary-tint: #4c8dff;
30 |
31 | --ion-color-secondary: #0cd1e8;
32 | --ion-color-secondary-rgb: 12, 209, 232;
33 | --ion-color-secondary-contrast: #ffffff;
34 | --ion-color-secondary-contrast-rgb: 255, 255, 255;
35 | --ion-color-secondary-shade: #0bb8cc;
36 | --ion-color-secondary-tint: #24d6ea;
37 |
38 | --ion-color-tertiary: #7044ff;
39 | --ion-color-tertiary-rgb: 112, 68, 255;
40 | --ion-color-tertiary-contrast: #ffffff;
41 | --ion-color-tertiary-contrast-rgb: 255, 255, 255;
42 | --ion-color-tertiary-shade: #633ce0;
43 | --ion-color-tertiary-tint: #7e57ff;
44 |
45 | --ion-color-success: #10dc60;
46 | --ion-color-success-rgb: 16, 220, 96;
47 | --ion-color-success-contrast: #ffffff;
48 | --ion-color-success-contrast-rgb: 255, 255, 255;
49 | --ion-color-success-shade: #0ec254;
50 | --ion-color-success-tint: #28e070;
51 |
52 | --ion-color-warning: #ffce00;
53 | --ion-color-warning-rgb: 255, 206, 0;
54 | --ion-color-warning-contrast: #ffffff;
55 | --ion-color-warning-contrast-rgb: 255, 255, 255;
56 | --ion-color-warning-shade: #e0b500;
57 | --ion-color-warning-tint: #ffd31a;
58 |
59 | --ion-color-danger: #f04141;
60 | --ion-color-danger-rgb: 245, 61, 61;
61 | --ion-color-danger-contrast: #ffffff;
62 | --ion-color-danger-contrast-rgb: 255, 255, 255;
63 | --ion-color-danger-shade: #d33939;
64 | --ion-color-danger-tint: #f25454;
65 |
66 | --ion-color-dark: #222428;
67 | --ion-color-dark-rgb: 34, 34, 34;
68 | --ion-color-dark-contrast: #ffffff;
69 | --ion-color-dark-contrast-rgb: 255, 255, 255;
70 | --ion-color-dark-shade: #1e2023;
71 | --ion-color-dark-tint: #383a3e;
72 |
73 | --ion-color-medium: #989aa2;
74 | --ion-color-medium-rgb: 152, 154, 162;
75 | --ion-color-medium-contrast: #ffffff;
76 | --ion-color-medium-contrast-rgb: 255, 255, 255;
77 | --ion-color-medium-shade: #86888f;
78 | --ion-color-medium-tint: #a2a4ab;
79 |
80 | --ion-color-light: #f4f5f8;
81 | --ion-color-light-rgb: 244, 244, 244;
82 | --ion-color-light-contrast: #000000;
83 | --ion-color-light-contrast-rgb: 0, 0, 0;
84 | --ion-color-light-shade: #d7d8da;
85 | --ion-color-light-tint: #f5f6f9;
86 |
87 | // Default Theme Colors
88 | --ion-background-color: #ffffff;
89 | --ion-background-color-rgb: 255,255,255;
90 |
91 | --ion-text-color: #000000;
92 | --ion-text-color-rgb: 0,0,0;
93 |
94 | --ion-color-step-50: #f2f2f2;
95 | --ion-color-step-100: #e6e6e6;
96 | --ion-color-step-150: #d9d9d9;
97 | --ion-color-step-200: #cccccc;
98 | --ion-color-step-250: #bfbfbf;
99 | --ion-color-step-300: #b3b3b3;
100 | --ion-color-step-350: #a6a6a6;
101 | --ion-color-step-400: #999999;
102 | --ion-color-step-450: #8c8c8c;
103 | --ion-color-step-500: #808080;
104 | --ion-color-step-550: #737373;
105 | --ion-color-step-600: #666666;
106 | --ion-color-step-650: #595959;
107 | --ion-color-step-700: #4d4d4d;
108 | --ion-color-step-750: #404040;
109 | --ion-color-step-800: #333333;
110 | --ion-color-step-850: #262626;
111 | --ion-color-step-900: #191919;
112 | --ion-color-step-950: #0d0d0d;
113 | }
114 |
115 | /*
116 | * Additional Ionic Colors
117 | * ----------------------------------------------------------------------------
118 | * In order to add colors to be used within Ionic components,
119 | * the color should be added as a class with the convention `.ion-color-{COLOR}`
120 | * where `{COLOR}` is the color to be used on the Ionic component.
121 | * For more information on adding new colors, please see
122 | * https://ionicframework.com/docs/theming/colors#adding-colors
123 | *
124 | * To generate the code for a new color, check out our new color creator:
125 | * https://ionicframework.com/docs/theming/colors#new-color-creator
126 | */
127 |
128 | :root {
129 | --ion-color-favorite: #69bb7b;
130 | --ion-color-favorite-rgb: 105,187,123;
131 | --ion-color-favorite-contrast: #ffffff;
132 | --ion-color-favorite-contrast-rgb: 255,255,255;
133 | --ion-color-favorite-shade: #5ca56c;
134 | --ion-color-favorite-tint: #78c288;
135 |
136 | --ion-color-twitter: #1da1f4;
137 | --ion-color-twitter-rgb: 29,161,244;
138 | --ion-color-twitter-contrast: #ffffff;
139 | --ion-color-twitter-contrast-rgb: 255,255,255;
140 | --ion-color-twitter-shade: #1a8ed7;
141 | --ion-color-twitter-tint: #34aaf5;
142 |
143 | --ion-color-google: #dc4a38;
144 | --ion-color-google-rgb: 220,74,56;
145 | --ion-color-google-contrast: #ffffff;
146 | --ion-color-google-contrast-rgb: 255,255,255;
147 | --ion-color-google-shade: #c24131;
148 | --ion-color-google-tint: #e05c4c;
149 |
150 | --ion-color-vimeo: #23b6ea;
151 | --ion-color-vimeo-rgb: 35,182,234;
152 | --ion-color-vimeo-contrast: #ffffff;
153 | --ion-color-vimeo-contrast-rgb: 255,255,255;
154 | --ion-color-vimeo-shade: #1fa0ce;
155 | --ion-color-vimeo-tint: #39bdec;
156 |
157 | --ion-color-facebook: #3b5998;
158 | --ion-color-facebook-rgb: 59,89,152;
159 | --ion-color-facebook-contrast: #ffffff;
160 | --ion-color-facebook-contrast-rgb: 255,255,255;
161 | --ion-color-facebook-shade: #344e86;
162 | --ion-color-facebook-tint: #4f6aa2;
163 | }
164 |
165 | .ion-color-favorite {
166 | --ion-color-base: var(--ion-color-favorite);
167 | --ion-color-base-rgb: var(--ion-color-favorite-rgb);
168 | --ion-color-contrast: var(--ion-color-favorite-contrast);
169 | --ion-color-contrast-rgb: var(--ion-color-favorite-contrast-rgb);
170 | --ion-color-shade: var(--ion-color-favorite-shade);
171 | --ion-color-tint: var(--ion-color-favorite-tint);
172 | }
173 |
174 | .ion-color-twitter {
175 | --ion-color-base: var(--ion-color-twitter);
176 | --ion-color-base-rgb: var(--ion-color-twitter-rgb);
177 | --ion-color-contrast: var(--ion-color-twitter-contrast);
178 | --ion-color-contrast-rgb: var(--ion-color-twitter-contrast-rgb);
179 | --ion-color-shade: var(--ion-color-twitter-shade);
180 | --ion-color-tint: var(--ion-color-twitter-tint);
181 | }
182 |
183 | .ion-color-google {
184 | --ion-color-base: var(--ion-color-google);
185 | --ion-color-base-rgb: var(--ion-color-google-rgb);
186 | --ion-color-contrast: var(--ion-color-google-contrast);
187 | --ion-color-contrast-rgb: var(--ion-color-google-contrast-rgb);
188 | --ion-color-shade: var(--ion-color-google-shade);
189 | --ion-color-tint: var(--ion-color-google-tint);
190 | }
191 |
192 | .ion-color-vimeo {
193 | --ion-color-base: var(--ion-color-vimeo);
194 | --ion-color-base-rgb: var(--ion-color-vimeo-rgb);
195 | --ion-color-contrast: var(--ion-color-vimeo-contrast);
196 | --ion-color-contrast-rgb: var(--ion-color-vimeo-contrast-rgb);
197 | --ion-color-shade: var(--ion-color-vimeo-shade);
198 | --ion-color-tint: var(--ion-color-vimeo-tint);
199 | }
200 |
201 | .ion-color-facebook {
202 | --ion-color-base: var(--ion-color-facebook);
203 | --ion-color-base-rgb: var(--ion-color-facebook-rgb);
204 | --ion-color-contrast: var(--ion-color-facebook-contrast);
205 | --ion-color-contrast-rgb: var(--ion-color-facebook-contrast-rgb);
206 | --ion-color-shade: var(--ion-color-facebook-shade);
207 | --ion-color-tint: var(--ion-color-facebook-tint);
208 | }
209 |
210 | .ion-color-github {
211 | --ion-color-base: #211F1F;
212 | --ion-color-base-rgb: 33,31,31;
213 | --ion-color-contrast: #ffffff;
214 | --ion-color-contrast-rgb: 255,255,255;
215 | --ion-color-shade: #1d1b1b;
216 | --ion-color-tint: #373535;
217 | }
218 |
219 | .ion-color-instagram {
220 | --ion-color-base: #9537BC;
221 | --ion-color-base-rgb: 149,55,188;
222 | --ion-color-contrast: #ffffff;
223 | --ion-color-contrast-rgb: 255,255,255;
224 | --ion-color-shade: #8330a5;
225 | --ion-color-tint: #a04bc3;
226 | }
227 |
228 | /*
229 | * Shared Variables
230 | * ----------------------------------------------------------------------------
231 | * To customize the look and feel of this app, you can override
232 | * the CSS variables found in Ionic's source files.
233 | * To view all of the possible Ionic variables, see:
234 | * https://ionicframework.com/docs/theming/css-variables#ionic-variables
235 | */
236 |
237 | :root {
238 | --ion-headings-font-weight: 300;
239 |
240 | --ion-color-angular: #ac282b;
241 | --ion-color-communication: #8e8d93;
242 | --ion-color-tooling: #fe4c52;
243 | --ion-color-services: #fd8b2d;
244 | --ion-color-design: #fed035;
245 | --ion-color-workshop: #69bb7b;
246 | --ion-color-food: #3bc7c4;
247 | --ion-color-documentation: #b16be3;
248 | --ion-color-navigation: #6600cc;
249 | }
250 |
251 | /*
252 | * App iOS Variables
253 | * ----------------------------------------------------------------------------
254 | * iOS only CSS variables can go here
255 | */
256 |
257 | .ios {
258 |
259 | }
260 |
261 | /*
262 | * App Material Design Variables
263 | * ----------------------------------------------------------------------------
264 | * Material Design only CSS variables can go here
265 | */
266 |
267 | .md {
268 |
269 | }
270 |
271 | /*
272 | * App Theme
273 | * ----------------------------------------------------------------------------
274 | * Ionic apps can have different themes applied, which can
275 | * then be further customized. These variables come last
276 | * so that the above variables are used by default.
277 | */
278 |
279 | /*
280 | * Dark Theme
281 | * ----------------------------------------------------------------------------
282 | */
283 |
284 | .dark-theme {
285 | --ion-color-primary: #428cff;
286 | --ion-color-primary-rgb: 66,140,255;
287 | --ion-color-primary-contrast: #ffffff;
288 | --ion-color-primary-contrast-rgb: 255,255,255;
289 | --ion-color-primary-shade: #3a7be0;
290 | --ion-color-primary-tint: #5598ff;
291 |
292 | --ion-color-secondary: #50c8ff;
293 | --ion-color-secondary-rgb: 80,200,255;
294 | --ion-color-secondary-contrast: #ffffff;
295 | --ion-color-secondary-contrast-rgb: 255,255,255;
296 | --ion-color-secondary-shade: #46b0e0;
297 | --ion-color-secondary-tint: #62ceff;
298 |
299 | --ion-color-tertiary: #6a64ff;
300 | --ion-color-tertiary-rgb: 106,100,255;
301 | --ion-color-tertiary-contrast: #ffffff;
302 | --ion-color-tertiary-contrast-rgb: 255,255,255;
303 | --ion-color-tertiary-shade: #5d58e0;
304 | --ion-color-tertiary-tint: #7974ff;
305 |
306 | --ion-color-success: #2fdf75;
307 | --ion-color-success-rgb: 47,223,117;
308 | --ion-color-success-contrast: #000000;
309 | --ion-color-success-contrast-rgb: 0,0,0;
310 | --ion-color-success-shade: #29c467;
311 | --ion-color-success-tint: #44e283;
312 |
313 | --ion-color-warning: #ffd534;
314 | --ion-color-warning-rgb: 255,213,52;
315 | --ion-color-warning-contrast: #000000;
316 | --ion-color-warning-contrast-rgb: 0,0,0;
317 | --ion-color-warning-shade: #e0bb2e;
318 | --ion-color-warning-tint: #ffd948;
319 |
320 | --ion-color-danger: #ff4961;
321 | --ion-color-danger-rgb: 255,73,97;
322 | --ion-color-danger-contrast: #ffffff;
323 | --ion-color-danger-contrast-rgb: 255,255,255;
324 | --ion-color-danger-shade: #e04055;
325 | --ion-color-danger-tint: #ff5b71;
326 |
327 | --ion-color-dark: #f4f5f8;
328 | --ion-color-dark-rgb: 244,245,248;
329 | --ion-color-dark-contrast: #000000;
330 | --ion-color-dark-contrast-rgb: 0,0,0;
331 | --ion-color-dark-shade: #d7d8da;
332 | --ion-color-dark-tint: #f5f6f9;
333 |
334 | --ion-color-medium: #989aa2;
335 | --ion-color-medium-rgb: 152,154,162;
336 | --ion-color-medium-contrast: #000000;
337 | --ion-color-medium-contrast-rgb: 0,0,0;
338 | --ion-color-medium-shade: #86888f;
339 | --ion-color-medium-tint: #a2a4ab;
340 |
341 | --ion-color-light: #222428;
342 | --ion-color-light-rgb: 34,36,40;
343 | --ion-color-light-contrast: #ffffff;
344 | --ion-color-light-contrast-rgb: 255,255,255;
345 | --ion-color-light-shade: #1e2023;
346 | --ion-color-light-tint: #383a3e;
347 | }
348 |
349 | /*
350 | * iOS Dark Theme
351 | * ----------------------------------------------------------------------------
352 | */
353 |
354 | .dark-theme.ios {
355 | --ion-background-color: #000000;
356 | --ion-background-color-rgb: 0,0,0;
357 |
358 | --ion-text-color: #ffffff;
359 | --ion-text-color-rgb: 255,255,255;
360 |
361 | --ion-color-step-50: #0d0d0d;
362 | --ion-color-step-100: #1a1a1a;
363 | --ion-color-step-150: #262626;
364 | --ion-color-step-200: #333333;
365 | --ion-color-step-250: #404040;
366 | --ion-color-step-300: #4d4d4d;
367 | --ion-color-step-350: #595959;
368 | --ion-color-step-400: #666666;
369 | --ion-color-step-450: #737373;
370 | --ion-color-step-500: #808080;
371 | --ion-color-step-550: #8c8c8c;
372 | --ion-color-step-600: #999999;
373 | --ion-color-step-650: #a6a6a6;
374 | --ion-color-step-700: #b3b3b3;
375 | --ion-color-step-750: #bfbfbf;
376 | --ion-color-step-800: #cccccc;
377 | --ion-color-step-850: #d9d9d9;
378 | --ion-color-step-900: #e6e6e6;
379 | --ion-color-step-950: #f2f2f2;
380 |
381 | --ion-toolbar-background: #0d0d0d;
382 |
383 | --ion-item-background: #1c1c1c;
384 | --ion-item-background-activated: #313131;
385 | }
386 |
387 |
388 | /*
389 | * Material Design Dark Theme
390 | * ----------------------------------------------------------------------------
391 | */
392 |
393 | .dark-theme.md {
394 | --ion-background-color: #121212;
395 | --ion-background-color-rgb: 18,18,18;
396 |
397 | --ion-text-color: #ffffff;
398 | --ion-text-color-rgb: 255,255,255;
399 |
400 | --ion-border-color: #222222;
401 |
402 | --ion-color-step-50: #1e1e1e;
403 | --ion-color-step-100: #2a2a2a;
404 | --ion-color-step-150: #363636;
405 | --ion-color-step-200: #414141;
406 | --ion-color-step-250: #4d4d4d;
407 | --ion-color-step-300: #595959;
408 | --ion-color-step-350: #656565;
409 | --ion-color-step-400: #717171;
410 | --ion-color-step-450: #7d7d7d;
411 | --ion-color-step-500: #898989;
412 | --ion-color-step-550: #949494;
413 | --ion-color-step-600: #a0a0a0;
414 | --ion-color-step-650: #acacac;
415 | --ion-color-step-700: #b8b8b8;
416 | --ion-color-step-750: #c4c4c4;
417 | --ion-color-step-800: #d0d0d0;
418 | --ion-color-step-850: #dbdbdb;
419 | --ion-color-step-900: #e7e7e7;
420 | --ion-color-step-950: #f3f3f3;
421 |
422 | --ion-item-background: #1e1e1e;
423 |
424 | --ion-toolbar-background: #1f1f1f;
425 |
426 | --ion-tab-bar-background: #1f1f1f;
427 | }
428 |
--------------------------------------------------------------------------------
/src/zone-flags.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Prevents Angular change detection from
3 | * running with certain Web Component callbacks
4 | */
5 | (window as any).__Zone_disable_customElements = true;
6 |
--------------------------------------------------------------------------------
/superstatic.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {
4 | "source": "**",
5 | "destination": "/index.html"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/app",
5 | "types": []
6 | },
7 | "files": [
8 | "src/main.ts",
9 | "src/polyfills.ts"
10 | ],
11 | "include": [
12 | "src/**/*.ts",
13 | "src/**/*.d.ts"
14 | ],
15 | "exclude": [
16 | "src/**/*.spec.ts",
17 | "src/test.ts",
18 | "src/environments/environment.prod.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": false,
7 | "declaration": false,
8 | "downlevelIteration": true,
9 | "experimentalDecorators": true,
10 | "module": "esnext",
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "lib": [
15 | "es2018",
16 | "dom"
17 | ]
18 | },
19 | "angularCompilerOptions": {
20 | "fullTemplateTypeCheck": true,
21 | "strictInjectionParameters": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-spacing": true,
20 | "indent": [
21 | true,
22 | "spaces"
23 | ],
24 | "interface-over-type-literal": true,
25 | "label-position": true,
26 | "max-line-length": [
27 | true,
28 | 140
29 | ],
30 | "member-access": false,
31 | "member-ordering": [
32 | true,
33 | {
34 | "order": [
35 | "static-field",
36 | "instance-field",
37 | "static-method",
38 | "instance-method"
39 | ]
40 | }
41 | ],
42 | "no-arg": true,
43 | "no-bitwise": true,
44 | "no-console": [
45 | true,
46 | "debug",
47 | "info",
48 | "time",
49 | "timeEnd",
50 | "trace"
51 | ],
52 | "no-construct": true,
53 | "no-debugger": true,
54 | "no-duplicate-super": true,
55 | "no-empty": false,
56 | "no-empty-interface": true,
57 | "no-eval": true,
58 | "no-inferrable-types": [
59 | true,
60 | "ignore-params"
61 | ],
62 | "no-misused-new": true,
63 | "no-non-null-assertion": true,
64 | "no-shadowed-variable": true,
65 | "no-string-literal": false,
66 | "no-string-throw": true,
67 | "no-switch-case-fall-through": true,
68 | "no-trailing-whitespace": true,
69 | "no-unnecessary-initializer": true,
70 | "no-unused-expression": true,
71 | "no-use-before-declare": true,
72 | "no-var-keyword": true,
73 | "object-literal-sort-keys": false,
74 | "one-line": [
75 | true,
76 | "check-open-brace",
77 | "check-catch",
78 | "check-else",
79 | "check-whitespace"
80 | ],
81 | "prefer-const": true,
82 | "quotemark": [
83 | true,
84 | "single"
85 | ],
86 | "radix": true,
87 | "semicolon": [
88 | true,
89 | "always"
90 | ],
91 | "triple-equals": [
92 | true,
93 | "allow-null-check"
94 | ],
95 | "typedef-whitespace": [
96 | true,
97 | {
98 | "call-signature": "nospace",
99 | "index-signature": "nospace",
100 | "parameter": "nospace",
101 | "property-declaration": "nospace",
102 | "variable-declaration": "nospace"
103 | }
104 | ],
105 | "unified-signatures": true,
106 | "variable-name": false,
107 | "whitespace": [
108 | true,
109 | "check-branch",
110 | "check-decl",
111 | "check-operator",
112 | "check-separator",
113 | "check-type"
114 | ],
115 | "directive-selector": [
116 | true,
117 | "attribute",
118 | "app",
119 | "camelCase"
120 | ],
121 | "component-selector": [
122 | true,
123 | "element",
124 | "app",
125 | "page",
126 | "kebab-case"
127 | ],
128 | "no-output-on-prefix": true,
129 | "no-inputs-metadata-property": true,
130 | "no-outputs-metadata-property": true,
131 | "no-host-metadata-property": true,
132 | "no-input-rename": true,
133 | "no-output-rename": true,
134 | "use-lifecycle-interface": true,
135 | "use-pipe-transform-interface": true,
136 | "directive-class-suffix": true
137 | }
138 | }
139 |
--------------------------------------------------------------------------------