├── src ├── app │ ├── pages │ │ ├── map │ │ │ ├── map.page.scss │ │ │ ├── map.module.ts │ │ │ ├── map.page.html │ │ │ ├── map.page.spec.ts │ │ │ └── map.page.ts │ │ ├── notifications │ │ │ ├── notifications.page.scss │ │ │ ├── notifications.module.ts │ │ │ ├── notifications.page.spec.ts │ │ │ ├── notifications.page.html │ │ │ └── notifications.page.ts │ │ ├── profile │ │ │ ├── profile.page.scss │ │ │ ├── profile.module.ts │ │ │ ├── profile.page.spec.ts │ │ │ ├── profile.page.ts │ │ │ └── profile.page.html │ │ └── pages.module.ts │ ├── services │ │ ├── ml.service.spec.ts │ │ ├── sms.service.spec.ts │ │ ├── cast.service.spec.ts │ │ ├── profile.service.spec.ts │ │ ├── weather.service.spec.ts │ │ ├── profile.service.ts │ │ ├── sms.service.ts │ │ ├── ml.service.ts │ │ ├── weather.service.ts │ │ ├── cast.service.ts │ │ └── test.json │ ├── auth │ │ ├── auth.service.spec.ts │ │ ├── auth.guard.spec.ts │ │ ├── auth.module.ts │ │ ├── auth.guard.ts │ │ └── auth.service.ts │ ├── app-routing.module.ts │ ├── authentication │ │ ├── login │ │ │ ├── login.page.spec.ts │ │ │ ├── login.module.ts │ │ │ ├── login.page.scss │ │ │ ├── login.page.ts │ │ │ └── login.page.html │ │ ├── register │ │ │ ├── register.page.spec.ts │ │ │ ├── register.module.ts │ │ │ ├── register.page.scss │ │ │ ├── register.page.html │ │ │ └── register.page.ts │ │ ├── forgot-password │ │ │ ├── forgot-password.page.spec.ts │ │ │ ├── forgot-password.page.ts │ │ │ ├── forgot-password.page.scss │ │ │ ├── forgot-password.module.ts │ │ │ └── forgot-password.page.html │ │ └── authentication.module.ts │ ├── app.component.html │ ├── app.module.ts │ ├── app.component.ts │ └── app.component.spec.ts ├── assets │ ├── icon │ │ └── favicon.png │ ├── icons │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ └── icon-512x512.png │ ├── images │ │ ├── backgrounds │ │ │ └── what-the-hex-dark.png │ │ ├── branding │ │ │ ├── lightning-alert-icon.svg │ │ │ └── sidebar-logo.svg │ │ └── icons │ │ │ └── cast.svg │ ├── sass │ │ └── bootport │ │ │ └── _sizing.scss │ └── shapes.svg ├── tsconfig.app.json ├── tsconfig.spec.json ├── main.ts ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── test.ts ├── global.scss ├── karma.conf.js ├── index.html ├── manifest.json ├── theme │ └── variables.scss └── polyfills.ts ├── .firebaserc ├── resources ├── icon.png ├── splash.png ├── ios │ ├── icon │ │ ├── icon.png │ │ ├── icon-40.png │ │ ├── icon-50.png │ │ ├── icon-60.png │ │ ├── icon-72.png │ │ ├── icon-76.png │ │ ├── icon@2x.png │ │ ├── icon-40@2x.png │ │ ├── icon-50@2x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72@2x.png │ │ ├── icon-76@2x.png │ │ ├── icon-small.png │ │ ├── icon-small@2x.png │ │ └── icon-small@3x.png │ └── splash │ │ ├── Default-667h.png │ │ ├── Default-736h.png │ │ ├── Default~iphone.png │ │ ├── Default@2x~iphone.png │ │ ├── Default-568h@2x~iphone.png │ │ ├── Default-Landscape-736h.png │ │ ├── Default-Landscape~ipad.png │ │ ├── Default-Portrait~ipad.png │ │ ├── Default-Landscape@2x~ipad.png │ │ └── Default-Portrait@2x~ipad.png ├── android │ ├── icon │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-ldpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ └── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ └── splash │ │ ├── drawable-land-screen.png │ │ ├── drawable-port-screen.png │ │ ├── drawable-land-hdpi-screen.png │ │ ├── drawable-land-ldpi-screen.png │ │ ├── drawable-land-mdpi-screen.png │ │ ├── drawable-land-xhdpi-screen.png │ │ ├── drawable-port-hdpi-screen.png │ │ ├── drawable-port-ldpi-screen.png │ │ ├── drawable-port-mdpi-screen.png │ │ ├── drawable-port-xhdpi-screen.png │ │ ├── drawable-land-xxhdpi-screen.png │ │ ├── drawable-land-xxxhdpi-screen.png │ │ ├── drawable-port-xxhdpi-screen.png │ │ └── drawable-port-xxxhdpi-screen.png └── README.md ├── ionic.config.json ├── firebase.json ├── e2e ├── tsconfig.e2e.json ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts └── protractor.conf.js ├── ngsw-config.json ├── tsconfig.json ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── tslint.json ├── angular.json └── config.xml /src/app/pages/map/map.page.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/pages/notifications/notifications.page.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "lightning-alert" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/icon.png -------------------------------------------------------------------------------- /resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/splash.png -------------------------------------------------------------------------------- /resources/ios/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon.png -------------------------------------------------------------------------------- /src/assets/icon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icon/favicon.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-40.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-50.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-60.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-72.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-76.png -------------------------------------------------------------------------------- /resources/ios/icon/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon@2x.png -------------------------------------------------------------------------------- /src/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /src/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-40@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-50@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-60@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-60@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-72@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-76@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-small.png -------------------------------------------------------------------------------- /src/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /src/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-small@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/icon/icon-small@3x.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-667h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default~iphone.png -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lightning-alert", 3 | "integrations": { 4 | "cordova": {} 5 | }, 6 | "type": "angular" 7 | } 8 | -------------------------------------------------------------------------------- /resources/ios/splash/Default@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-568h@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-568h@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-Landscape-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-Landscape~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-Portrait~ipad.png -------------------------------------------------------------------------------- /resources/android/icon/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/icon/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /resources/android/icon/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/icon/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /resources/android/icon/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/icon/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-screen.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-Landscape@2x~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/ios/splash/Default-Portrait@2x~ipad.png -------------------------------------------------------------------------------- /resources/android/icon/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/icon/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /resources/android/icon/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/icon/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/assets/images/backgrounds/what-the-hex-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/src/assets/images/backgrounds/what-the-hex-dark.png -------------------------------------------------------------------------------- /resources/android/icon/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/icon/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-hdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-ldpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-mdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-xhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-hdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-ldpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-mdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-xhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-xxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-land-xxxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-xxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HEXcube/LightningAlert/HEAD/resources/android/splash/drawable-port-xxxhdpi-screen.png -------------------------------------------------------------------------------- /src/app/pages/profile/profile.page.scss: -------------------------------------------------------------------------------- 1 | .custom-title { 2 | margin-top: 2em; 3 | } 4 | 5 | .custom-button-area { 6 | padding-top: 0.5em; 7 | padding-bottom: 0.5em; 8 | } 9 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "www", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/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 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(destination) { 5 | return browser.get(destination); 6 | } 7 | 8 | getTitle() { 9 | return browser.getTitle(); 10 | } 11 | 12 | getPageOneTitleText() { 13 | return element(by.tagName('app-home')).element(by.deepCss('ion-title')).getText(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/services/ml.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { MlService } from './ml.service'; 4 | 5 | describe('MlService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: MlService = TestBed.get(MlService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/auth/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | 5 | describe('AuthService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: AuthService = TestBed.get(AuthService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/sms.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SmsService } from './sms.service'; 4 | 5 | describe('SmsService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: SmsService = TestBed.get(SmsService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/cast.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { CastService } from './cast.service'; 4 | 5 | describe('CastService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: CastService = TestBed.get(CastService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/profile.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ProfileService } from './profile.service'; 4 | 5 | describe('ProfileService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ProfileService = TestBed.get(ProfileService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/weather.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { WeatherService } from './weather.service'; 4 | 5 | describe('WeatherService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: WeatherService = TestBed.get(WeatherService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/auth/auth.guard.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async, inject } from '@angular/core/testing'; 2 | 3 | import { AuthGuard } from './auth.guard'; 4 | 5 | describe('AuthGuard', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthGuard] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([AuthGuard], (guard: AuthGuard) => { 13 | expect(guard).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /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() 12 | .bootstrapModule(AppModule) 13 | .catch(err => console.log(err)); 14 | -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | These are Cordova resources. You can replace icon.png and splash.png and run 2 | `ionic cordova resources` to generate custom icons and splash screens for your 3 | app. See `ionic cordova resources --help` for details. 4 | 5 | Cordova reference documentation: 6 | 7 | - Icons: https://cordova.apache.org/docs/en/latest/config_ref/images.html 8 | - Splash Screens: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/ 9 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "/index.html", 3 | "assetGroups": [ 4 | { 5 | "name": "Lightning Alert", 6 | "installMode": "prefetch", 7 | "resources": { 8 | "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"] 9 | } 10 | }, 11 | { 12 | "name": "assets", 13 | "installMode": "lazy", 14 | "updateMode": "prefetch", 15 | "resources": { 16 | "files": ["/assets/**"] 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('new App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | describe('default screen', () => { 10 | beforeEach(() => { 11 | page.navigateTo('/home'); 12 | }); 13 | it('should have a title saying Home', () => { 14 | page.getPageOneTitleText().then(title => { 15 | expect(title).toEqual('Home'); 16 | }); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2018", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | *~ 5 | *.sw[mnpcod] 6 | *.log 7 | *.tmp 8 | *.tmp.* 9 | log.txt 10 | *.sublime-project 11 | *.sublime-workspace 12 | .vscode/ 13 | npm-debug.log* 14 | 15 | .idea/ 16 | .ionic/ 17 | .sourcemaps/ 18 | .sass-cache/ 19 | .tmp/ 20 | .versions/ 21 | coverage/ 22 | www/ 23 | node_modules/ 24 | tmp/ 25 | temp/ 26 | platforms/ 27 | plugins/ 28 | plugins/android.json 29 | plugins/ios.json 30 | $RECYCLE.BIN/ 31 | 32 | .DS_Store 33 | Thumbs.db 34 | UserInterfaceState.xcuserstate 35 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | firebase: { 4 | apiKey: 'AIzaSyCWcmR_LdwsYH3K7WgmPIN2Cd4U6PONS-k', 5 | authDomain: 'lightning-alert.firebaseapp.com', 6 | databaseURL: 'https://lightning-alert.firebaseio.com', 7 | projectId: 'lightning-alert', 8 | storageBucket: 'lightning-alert.appspot.com', 9 | messagingSenderId: '224662572143' 10 | }, 11 | SMSServerUrl: 'http://localhost:8100/', 12 | MLServerURL: 'http://localhost:5000/', 13 | darkSkyURL: 14 | 'https://api.darksky.net/forecast/f12d2349c8b1724ca4e693a295f29516/' 15 | }; 16 | -------------------------------------------------------------------------------- /src/app/pages/map/map.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | 6 | import { IonicModule } from '@ionic/angular'; 7 | 8 | import { MapPage } from './map.page'; 9 | 10 | const routes: Routes = [ 11 | { 12 | path: '', 13 | component: MapPage 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [ 19 | CommonModule, 20 | FormsModule, 21 | IonicModule, 22 | RouterModule.forChild(routes) 23 | ], 24 | declarations: [MapPage] 25 | }) 26 | export class MapPageModule {} 27 | -------------------------------------------------------------------------------- /src/app/pages/map/map.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Lightning Alert 14 | 15 | 16 | 17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { AuthGuard } from './auth/auth.guard'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | redirectTo: 'authentication', 9 | pathMatch: 'full' 10 | }, 11 | { 12 | path: 'authentication', 13 | loadChildren: './authentication/authentication.module#AuthenticationModule' 14 | }, 15 | { 16 | path: 'pages', 17 | canActivate: [AuthGuard], 18 | loadChildren: './pages/pages.module#PagesModule' 19 | } 20 | ]; 21 | 22 | @NgModule({ 23 | imports: [RouterModule.forRoot(routes)], 24 | exports: [RouterModule] 25 | }) 26 | export class AppRoutingModule {} 27 | -------------------------------------------------------------------------------- /src/app/services/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AngularFirestore } from '@angular/fire/firestore'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class ProfileService { 8 | constructor(private firestore: AngularFirestore) {} 9 | 10 | getProfile(uid: string) { 11 | return this.firestore 12 | .collection('Profiles') 13 | .doc(uid) 14 | .snapshotChanges(); 15 | } 16 | 17 | createProfile(Profile: any) { 18 | return this.firestore.collection('Profiles').add(Profile); 19 | } 20 | 21 | deleteProfile(uid: string) { 22 | return this.firestore 23 | .collection('Profiles') 24 | .doc(uid) 25 | .delete(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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/app/pages/notifications/notifications.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | 6 | import { IonicModule } from '@ionic/angular'; 7 | 8 | import { NotificationsPage } from './notifications.page'; 9 | 10 | const routes: Routes = [ 11 | { 12 | path: '', 13 | component: NotificationsPage 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [ 19 | CommonModule, 20 | FormsModule, 21 | IonicModule, 22 | RouterModule.forChild(routes) 23 | ], 24 | declarations: [NotificationsPage] 25 | }) 26 | export class NotificationsPageModule {} 27 | -------------------------------------------------------------------------------- /src/app/pages/pages.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | 5 | const routes = [ 6 | { 7 | path: '', 8 | redirectTo: 'map', 9 | pathMatch: 'full' 10 | }, 11 | { path: 'map', loadChildren: './map/map.module#MapPageModule' }, 12 | { path: 'profile', loadChildren: './profile/profile.module#ProfilePageModule' }, 13 | { path: 'notifications', loadChildren: './notifications/notifications.module#NotificationsPageModule' } 14 | ]; 15 | @NgModule({ 16 | imports: [RouterModule.forChild(routes), CommonModule], 17 | exports: [RouterModule], 18 | declarations: [] 19 | }) 20 | export class PagesModule {} 21 | -------------------------------------------------------------------------------- /src/app/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { AuthGuard } from './auth.guard'; 4 | import { AuthService } from './auth.service'; 5 | import { AngularFireModule } from '@angular/fire'; 6 | import { environment } from 'src/environments/environment'; 7 | import { AngularFireAuthModule } from '@angular/fire/auth'; 8 | import { HttpClientModule } from '@angular/common/http'; 9 | 10 | @NgModule({ 11 | declarations: [], 12 | imports: [ 13 | AngularFireModule.initializeApp(environment.firebase), 14 | AngularFireAuthModule, 15 | CommonModule, 16 | HttpClientModule 17 | ], 18 | providers: [AuthGuard, AuthService] 19 | }) 20 | export class AuthModule {} 21 | -------------------------------------------------------------------------------- /src/app/pages/profile/profile.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | 6 | import { IonicModule } from '@ionic/angular'; 7 | 8 | import { ProfilePage } from './profile.page'; 9 | import { AngularFirestoreModule } from '@angular/fire/firestore'; 10 | 11 | const routes: Routes = [ 12 | { 13 | path: '', 14 | component: ProfilePage 15 | } 16 | ]; 17 | 18 | @NgModule({ 19 | imports: [ 20 | CommonModule, 21 | FormsModule, 22 | IonicModule, 23 | RouterModule.forChild(routes), 24 | AngularFirestoreModule 25 | ], 26 | declarations: [ProfilePage] 27 | }) 28 | export class ProfilePageModule {} 29 | -------------------------------------------------------------------------------- /src/app/pages/map/map.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { MapPage } from './map.page'; 5 | 6 | describe('MapPage', () => { 7 | let component: MapPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ MapPage ], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(MapPage); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/auth/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | CanActivate, 4 | ActivatedRouteSnapshot, 5 | RouterStateSnapshot, 6 | Router 7 | } from '@angular/router'; 8 | import { Observable } from 'rxjs'; 9 | import { AuthService } from './auth.service'; 10 | 11 | @Injectable({ 12 | providedIn: 'root' 13 | }) 14 | export class AuthGuard implements CanActivate { 15 | constructor(private authService: AuthService, private router: Router) {} 16 | canActivate( 17 | next: ActivatedRouteSnapshot, 18 | state: RouterStateSnapshot 19 | ): Observable | Promise | boolean { 20 | if (this.authService.isAuthenticated()) { 21 | return true; 22 | } else { 23 | this.router.navigate(['authentication']); 24 | return false; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/authentication/login/login.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { LoginPage } from './login.page'; 5 | 6 | describe('LoginPage', () => { 7 | let component: LoginPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ LoginPage ], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(LoginPage); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/pages/profile/profile.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { ProfilePage } from './profile.page'; 5 | 6 | describe('ProfilePage', () => { 7 | let component: ProfilePage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ ProfilePage ], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(ProfilePage); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/app/authentication/register/register.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { RegisterPage } from './register.page'; 5 | 6 | describe('RegisterPage', () => { 7 | let component: RegisterPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ RegisterPage ], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(RegisterPage); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/assets/images/branding/lightning-alert-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/global.scss: -------------------------------------------------------------------------------- 1 | // http://ionicframework.com/docs/theming/ 2 | @import '~@ionic/angular/css/core.css'; 3 | @import '~@ionic/angular/css/normalize.css'; 4 | @import '~@ionic/angular/css/structure.css'; 5 | @import '~@ionic/angular/css/typography.css'; 6 | 7 | @import '~@ionic/angular/css/padding.css'; 8 | @import '~@ionic/angular/css/float-elements.css'; 9 | @import '~@ionic/angular/css/text-alignment.css'; 10 | @import '~@ionic/angular/css/text-transformation.css'; 11 | @import '~@ionic/angular/css/flex-utils.css'; 12 | 13 | @import 'assets/sass//bootport/sizing.scss'; 14 | 15 | ion-menu { 16 | ion-toolbar .sidebar-logo { 17 | line-height: 0; 18 | max-width: 100%; 19 | height: auto; 20 | } 21 | 22 | ion-content .sidebar-icon { 23 | color: dodgerblue; 24 | } 25 | } 26 | 27 | .custom-toolbar { 28 | --ion-color-secondary: dodgerblue; 29 | } 30 | -------------------------------------------------------------------------------- /src/app/pages/notifications/notifications.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { NotificationsPage } from './notifications.page'; 5 | 6 | describe('NotificationsPage', () => { 7 | let component: NotificationsPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ NotificationsPage ], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(NotificationsPage); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/authentication/forgot-password/forgot-password.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { ForgotPasswordPage } from './forgot-password.page'; 5 | 6 | describe('ForgotPasswordPage', () => { 7 | let component: ForgotPasswordPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ ForgotPasswordPage ], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(ForgotPasswordPage); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/assets/sass/bootport/_sizing.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // This variable affects the `.h-*` and `.w-*` classes. 4 | $sizes: () !default; 5 | // stylelint-disable-next-line scss/dollar-variable-default 6 | $sizes: map-merge( 7 | ( 8 | 25: 25%, 9 | 50: 50%, 10 | 75: 75%, 11 | 100: 100%, 12 | auto: auto 13 | ), 14 | $sizes 15 | ); 16 | 17 | // Width and height 18 | 19 | @each $prop, $abbrev in (width: w, height: h) { 20 | @each $size, $length in $sizes { 21 | .#{$abbrev}-#{$size} { #{$prop}: $length !important; } 22 | } 23 | } 24 | 25 | .mw-100 { max-width: 100% !important; } 26 | .mh-100 { max-height: 100% !important; } 27 | 28 | // Viewport additional helpers 29 | 30 | .min-vw-100 { min-width: 100vw !important; } 31 | .min-vh-100 { min-height: 100vh !important; } 32 | 33 | .vw-100 { width: 100vw !important; } 34 | .vh-100 { height: 100vh !important; } 35 | -------------------------------------------------------------------------------- /src/app/services/sms.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AngularFirestore } from '@angular/fire/firestore'; 3 | import { environment } from 'src/environments/environment'; 4 | import { HttpHeaders, HttpClient } from '@angular/common/http'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class SmsService { 10 | constructor(private firestore: AngularFirestore, private http: HttpClient) {} 11 | getHeaders() { 12 | const headers = new HttpHeaders(); 13 | headers.append('Content-Type', 'application/json'); 14 | return headers; 15 | } 16 | 17 | sendSMS() { 18 | const options = { 19 | headers: this.getHeaders() 20 | }; 21 | 22 | return this.http.post(`${environment.SMSServerUrl}`, options).subscribe( 23 | val => { 24 | console.log(val); 25 | }, 26 | err => { 27 | console.log(err); 28 | } 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/services/ml.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { environment } from 'src/environments/environment'; 3 | import { AngularFirestore } from '@angular/fire/firestore'; 4 | import { HttpHeaders, HttpClient } from '@angular/common/http'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class MlService { 10 | constructor(private firestore: AngularFirestore, private http: HttpClient) {} 11 | getHeaders() { 12 | const headers = new HttpHeaders(); 13 | headers.append('Content-Type', 'application/json'); 14 | return headers; 15 | } 16 | 17 | getPrediction() { 18 | const options = { 19 | headers: this.getHeaders() 20 | }; 21 | 22 | return this.http 23 | .post(`${environment.MLServerURL}api`, { exp: 1.8 }, options) 24 | .subscribe( 25 | val => { 26 | console.log(val); 27 | }, 28 | err => { 29 | console.log(err); 30 | } 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Team Hypercube 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{ p.title }} 15 | 16 | 17 | 18 | 19 | 20 | Logout 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/assets/shapes.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/pages/notifications/notifications.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Notifications 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | 24 | 30 | 31 |

{{ card.description }}

32 |
33 |
34 |
35 |
36 |
37 |
38 |
-------------------------------------------------------------------------------- /src/app/authentication/register/register.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | 6 | import { IonicModule } from '@ionic/angular'; 7 | 8 | import { RegisterPage } from './register.page'; 9 | import { AngularFireAuthModule } from '@angular/fire/auth'; 10 | import { AngularFireModule } from '@angular/fire'; 11 | import { environment } from 'src/environments/environment'; 12 | import { AngularFirestoreModule } from '@angular/fire/firestore'; 13 | import { AngularFireStorageModule } from '@angular/fire/storage'; 14 | 15 | const routes: Routes = [ 16 | { 17 | path: '', 18 | component: RegisterPage 19 | } 20 | ]; 21 | 22 | @NgModule({ 23 | imports: [ 24 | CommonModule, 25 | FormsModule, 26 | ReactiveFormsModule, 27 | AngularFireAuthModule, 28 | IonicModule, 29 | RouterModule.forChild(routes), 30 | AngularFireModule.initializeApp(environment.firebase), 31 | AngularFirestoreModule, 32 | AngularFireStorageModule 33 | ], 34 | declarations: [RegisterPage] 35 | }) 36 | export class RegisterPageModule {} 37 | -------------------------------------------------------------------------------- /src/app/authentication/forgot-password/forgot-password.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, FormBuilder, Validators } from '@angular/forms'; 3 | import { AuthService } from 'src/app/auth/auth.service'; 4 | import { Router, ActivatedRoute } from '@angular/router'; 5 | import { NavController } from '@ionic/angular'; 6 | 7 | @Component({ 8 | selector: 'app-forgot-password', 9 | templateUrl: './forgot-password.page.html', 10 | styleUrls: ['./forgot-password.page.scss'] 11 | }) 12 | export class ForgotPasswordPage implements OnInit { 13 | form: FormGroup; 14 | constructor( 15 | private fb: FormBuilder, 16 | private authService: AuthService, 17 | private router: Router, 18 | private route: ActivatedRoute, 19 | private nav: NavController 20 | ) {} 21 | ngOnInit() { 22 | this.form = this.fb.group({ 23 | email: ['', Validators.required] 24 | }); 25 | } 26 | 27 | onSubmit() { 28 | const { email } = this.form.value; 29 | this.authService.resetPassword(email); 30 | } 31 | 32 | onLogin() { 33 | this.nav.goBack(); 34 | } 35 | 36 | onRegister() { 37 | this.router.navigate(['register'], { relativeTo: this.route.parent }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/authentication/forgot-password/forgot-password.page.scss: -------------------------------------------------------------------------------- 1 | .background-image { 2 | background-image: linear-gradient( 3 | to bottom, 4 | rgba(black, 0), 5 | rgba(black, 0.0625) 6 | ), 7 | url('../../../assets/images/backgrounds/what-the-hex-dark.png'); 8 | background-repeat: repeat; 9 | background-position: initial; 10 | background-size: initial; 11 | background-attachment: fixed; 12 | } 13 | 14 | .brand-image { 15 | border-radius: 2em; 16 | max-width: 50%; 17 | overflow: hidden; 18 | line-height: 0; 19 | margin-left: auto; 20 | margin-right: auto; 21 | margin-bottom: 1em; 22 | } 23 | 24 | ion-item.transparent-input { 25 | --ion-item-background: transparent; 26 | --border-color: rgba(255, 255, 255, 0.75); 27 | --highlight-color-focused: white; 28 | } 29 | 30 | .custom-link { 31 | text-decoration: none; 32 | color: dodgerblue; 33 | transition: 0.4s; 34 | 35 | &:hover { 36 | text-decoration: underline; 37 | color: white; 38 | } 39 | } 40 | 41 | .signup-icon { 42 | vertical-align: middle; 43 | margin-bottom: 0.125em; 44 | } 45 | 46 | .send-button { 47 | --background: dodgerblue; 48 | --background-focused: royalblue; 49 | --background-activated: royalblue; 50 | } 51 | -------------------------------------------------------------------------------- /src/assets/images/branding/sidebar-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | firebase: { 8 | apiKey: 'AIzaSyCWcmR_LdwsYH3K7WgmPIN2Cd4U6PONS-k', 9 | authDomain: 'lightning-alert.firebaseapp.com', 10 | databaseURL: 'https://lightning-alert.firebaseio.com', 11 | projectId: 'lightning-alert', 12 | storageBucket: 'lightning-alert.appspot.com', 13 | messagingSenderId: '224662572143' 14 | }, 15 | SMSServerUrl: 'http://localhost:8100/', 16 | MLServerURL: 'http://localhost:5000/', 17 | darkSkyURL: 18 | 'https://api.darksky.net/forecast/f12d2349c8b1724ca4e693a295f29516/' 19 | }; 20 | 21 | /* 22 | * For easier debugging in development mode, you can import the following file 23 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 24 | * 25 | * This import should be commented out in production mode because it will have a negative impact 26 | * on performance if an error is thrown. 27 | */ 28 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 29 | -------------------------------------------------------------------------------- /src/app/authentication/login/login.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | 6 | import { IonicModule } from '@ionic/angular'; 7 | 8 | import { LoginPage } from './login.page'; 9 | import { AngularFireAuthModule } from '@angular/fire/auth'; 10 | import { AngularFireModule } from '@angular/fire'; 11 | import { environment } from 'src/environments/environment'; 12 | import { AngularFirestoreModule } from '@angular/fire/firestore'; 13 | import { AngularFireStorageModule } from '@angular/fire/storage'; 14 | 15 | const routes: Routes = [ 16 | { 17 | path: '', 18 | component: LoginPage 19 | } 20 | ]; 21 | 22 | @NgModule({ 23 | imports: [ 24 | CommonModule, 25 | FormsModule, 26 | ReactiveFormsModule, 27 | AngularFireAuthModule, 28 | IonicModule, 29 | RouterModule.forChild(routes), 30 | AngularFireModule.initializeApp(environment.firebase), 31 | AngularFirestoreModule, 32 | AngularFireStorageModule, 33 | IonicModule, 34 | RouterModule.forChild(routes) 35 | ], 36 | declarations: [LoginPage] 37 | }) 38 | export class LoginPageModule {} 39 | -------------------------------------------------------------------------------- /src/assets/images/icons/cast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_cast_black_24dp 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ionic App 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Lightning Alert", 3 | "short_name": "lightning alert", 4 | "theme_color": "#1976d2", 5 | "background_color": "#fafafa", 6 | "display": "standalone", 7 | "scope": "/", 8 | "start_url": "/", 9 | "icons": [ 10 | { 11 | "src": "assets/icons/icon-72x72.png", 12 | "sizes": "72x72", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "assets/icons/icon-96x96.png", 17 | "sizes": "96x96", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "assets/icons/icon-128x128.png", 22 | "sizes": "128x128", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "assets/icons/icon-144x144.png", 27 | "sizes": "144x144", 28 | "type": "image/png" 29 | }, 30 | { 31 | "src": "assets/icons/icon-152x152.png", 32 | "sizes": "152x152", 33 | "type": "image/png" 34 | }, 35 | { 36 | "src": "assets/icons/icon-192x192.png", 37 | "sizes": "192x192", 38 | "type": "image/png" 39 | }, 40 | { 41 | "src": "assets/icons/icon-384x384.png", 42 | "sizes": "384x384", 43 | "type": "image/png" 44 | }, 45 | { 46 | "src": "assets/icons/icon-512x512.png", 47 | "sizes": "512x512", 48 | "type": "image/png" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { RouteReuseStrategy } from '@angular/router'; 4 | 5 | import { IonicModule, IonicRouteStrategy, NavParams } from '@ionic/angular'; 6 | import { SplashScreen } from '@ionic-native/splash-screen/ngx'; 7 | import { StatusBar } from '@ionic-native/status-bar/ngx'; 8 | 9 | import { AppComponent } from './app.component'; 10 | import { AppRoutingModule } from './app-routing.module'; 11 | import { AuthModule } from './auth/auth.module'; 12 | import { ServiceWorkerModule } from '@angular/service-worker'; 13 | import { environment } from '../environments/environment'; 14 | import { AngularFirestoreModule } from '@angular/fire/firestore'; 15 | 16 | @NgModule({ 17 | declarations: [AppComponent], 18 | entryComponents: [], 19 | imports: [ 20 | BrowserModule, 21 | IonicModule.forRoot(), 22 | AppRoutingModule, 23 | AuthModule, 24 | ServiceWorkerModule.register('ngsw-worker.js', { 25 | enabled: environment.production 26 | }), 27 | AngularFirestoreModule 28 | ], 29 | providers: [ 30 | StatusBar, 31 | SplashScreen, 32 | { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } 33 | ], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule {} 37 | -------------------------------------------------------------------------------- /src/app/authentication/forgot-password/forgot-password.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { Routes, RouterModule } from '@angular/router'; 5 | 6 | import { IonicModule } from '@ionic/angular'; 7 | 8 | import { ForgotPasswordPage } from './forgot-password.page'; 9 | import { AngularFireAuthModule } from '@angular/fire/auth'; 10 | import { AngularFireModule } from '@angular/fire'; 11 | import { environment } from 'src/environments/environment'; 12 | import { AngularFirestoreModule } from '@angular/fire/firestore'; 13 | import { AngularFireStorageModule } from '@angular/fire/storage'; 14 | 15 | const routes: Routes = [ 16 | { 17 | path: '', 18 | component: ForgotPasswordPage 19 | } 20 | ]; 21 | 22 | @NgModule({ 23 | imports: [ 24 | CommonModule, 25 | FormsModule, 26 | ReactiveFormsModule, 27 | AngularFireAuthModule, 28 | IonicModule, 29 | RouterModule.forChild(routes), 30 | AngularFireModule.initializeApp(environment.firebase), 31 | AngularFirestoreModule, 32 | AngularFireStorageModule, 33 | IonicModule, 34 | RouterModule.forChild(routes) 35 | ], 36 | declarations: [ForgotPasswordPage] 37 | }) 38 | export class ForgotPasswordPageModule {} 39 | -------------------------------------------------------------------------------- /src/app/authentication/authentication.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | import { ReactiveFormsModule, FormsModule } from '@angular/forms'; 5 | import { AngularFireAuthModule } from '@angular/fire/auth'; 6 | import { IonicModule } from '@ionic/angular'; 7 | import { AngularFireModule } from '@angular/fire'; 8 | import { AngularFirestoreModule } from '@angular/fire/firestore'; 9 | import { AngularFireStorageModule } from '@angular/fire/storage'; 10 | import { environment } from 'src/environments/environment'; 11 | import { AuthModule } from '../auth/auth.module'; 12 | 13 | const routes = [ 14 | { 15 | path: '', 16 | redirectTo: 'login', 17 | pathMatch: 'full' 18 | }, 19 | { 20 | path: 'login', 21 | loadChildren: './login/login.module#LoginPageModule' 22 | }, 23 | { 24 | path: 'forgot-password', 25 | loadChildren: 26 | './forgot-password/forgot-password.module#ForgotPasswordPageModule' 27 | }, 28 | { 29 | path: 'register', 30 | loadChildren: './register/register.module#RegisterPageModule' 31 | } 32 | ]; 33 | @NgModule({ 34 | imports: [RouterModule.forChild(routes), CommonModule], 35 | exports: [RouterModule], 36 | declarations: [] 37 | }) 38 | export class AuthenticationModule {} 39 | -------------------------------------------------------------------------------- /src/app/services/weather.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AngularFirestore } from '@angular/fire/firestore'; 3 | import { HttpHeaders, HttpClient } from '@angular/common/http'; 4 | import { environment } from 'src/environments/environment'; 5 | import Darksky from 'darkskyjs'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class WeatherService { 11 | constructor(private firestore: AngularFirestore, private http: HttpClient) {} 12 | 13 | private getHeaders() { 14 | const headers = new HttpHeaders(); 15 | headers.append('Access-Control-Allow-Origin', '*'); 16 | headers.append('Content-Type', 'application/json'); 17 | headers.append('Access-Control-Allow-Headers', '*'); 18 | return headers; 19 | } 20 | 21 | getCurrentWeatherDetails(latitude: any, longitude: any) { 22 | Darksky.getCurrentConditions( 23 | [ 24 | // location object(s) 25 | { 26 | latitude, 27 | longitude, 28 | name: 'London' 29 | } 30 | ], 31 | // callback 32 | function(conditions) { 33 | for (let i = 0, length = conditions.length; i < length; i++) { 34 | if (conditions[i].name === 'London') { 35 | console.log(conditions[i].cloudCover()); 36 | } 37 | } 38 | } 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/authentication/login/login.page.scss: -------------------------------------------------------------------------------- 1 | .background-image { 2 | background-image: linear-gradient( 3 | to bottom, 4 | rgba(black, 0), 5 | rgba(black, 0.0625) 6 | ), 7 | url('../../../assets/images/backgrounds/what-the-hex-dark.png'); 8 | background-repeat: repeat; 9 | background-position: initial; 10 | background-size: initial; 11 | background-attachment: fixed; 12 | } 13 | 14 | .brand-image { 15 | border-radius: 2em; 16 | max-width: 50%; 17 | overflow: hidden; 18 | line-height: 0; 19 | margin-left: auto; 20 | margin-right: auto; 21 | margin-bottom: 1em; 22 | } 23 | 24 | ion-item.transparent-input { 25 | --ion-item-background: transparent; 26 | --border-color: rgba(255, 255, 255, 0.75); 27 | --highlight-color-focused: white; 28 | } 29 | 30 | .custom-link { 31 | text-decoration: none; 32 | color: dodgerblue; 33 | transition: 0.4s; 34 | 35 | &:hover { 36 | text-decoration: underline; 37 | color: white; 38 | } 39 | } 40 | 41 | .signup-icon { 42 | vertical-align: middle; 43 | margin-bottom: 0.125em; 44 | } 45 | 46 | .login-button { 47 | --background: dodgerblue; 48 | --background-focused: mediumblue; 49 | --background-activated: mediumblue; 50 | 51 | .login-icon { 52 | margin-bottom: 0.125em; 53 | } 54 | } 55 | 56 | // ion-button { 57 | // font-weight: 300; 58 | // } 59 | -------------------------------------------------------------------------------- /src/app/authentication/register/register.page.scss: -------------------------------------------------------------------------------- 1 | .background-image { 2 | background-image: linear-gradient( 3 | to bottom, 4 | rgba(black, 0), 5 | rgba(black, 0.0625) 6 | ), 7 | url('../../../assets/images/backgrounds/what-the-hex-dark.png'); 8 | background-repeat: repeat; 9 | background-position: initial; 10 | background-size: initial; 11 | background-attachment: fixed; 12 | } 13 | 14 | .brand-image { 15 | border-radius: 2em; 16 | max-width: 50%; 17 | overflow: hidden; 18 | line-height: 0; 19 | margin-left: auto; 20 | margin-right: auto; 21 | margin-bottom: 1em; 22 | } 23 | 24 | ion-item.transparent-input { 25 | --ion-item-background: transparent; 26 | --border-color: rgba(255, 255, 255, 0.75); 27 | --highlight-color-focused: white; 28 | } 29 | 30 | .custom-link { 31 | text-decoration: none; 32 | color: dodgerblue; 33 | transition: 0.4s; 34 | 35 | &:hover { 36 | text-decoration: underline; 37 | color: white; 38 | } 39 | } 40 | 41 | .signup-icon { 42 | vertical-align: middle; 43 | margin-bottom: 0.125em; 44 | } 45 | 46 | .login-button { 47 | --background: dodgerblue; 48 | --background-focused: royalblue; 49 | --background-activated: royalblue; 50 | 51 | .login-icon { 52 | margin-bottom: 0.125em; 53 | } 54 | } 55 | 56 | // ion-button { 57 | // font-weight: 300; 58 | // } 59 | -------------------------------------------------------------------------------- /src/app/pages/notifications/notifications.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { MenuController } from '@ionic/angular'; 3 | 4 | @Component({ 5 | selector: 'app-notifications', 6 | templateUrl: './notifications.page.html', 7 | styleUrls: ['./notifications.page.scss'], 8 | }) 9 | export class NotificationsPage implements OnInit { 10 | public listCards = [ 11 | { 12 | colorName: 'danger', 13 | iconName: 'alert', 14 | description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.' 15 | }, 16 | { 17 | colorName: 'primary', 18 | iconName: 'square', 19 | description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.' 20 | }, 21 | { 22 | colorName: 'warning', 23 | iconName: 'sunny', 24 | description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.' 25 | }, 26 | { 27 | colorName: 'primary', 28 | iconName: 'square', 29 | description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.' 30 | } 31 | ]; 32 | 33 | constructor( 34 | public menuCtrl: MenuController 35 | ) {} 36 | 37 | ngOnInit() { 38 | } 39 | 40 | /* 41 | Disable sidebar 42 | https://stackoverflow.com/questions/51610075/disable-menu-on-login-page-ionic-4/51637860#51637860 43 | */ 44 | ionViewWillEnter() { 45 | this.menuCtrl.enable(false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/authentication/forgot-password/forgot-password.page.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 9 |
10 |
11 | 12 | 18 | 19 |
20 |
21 | 22 | Send Recovery Email 23 | 29 | 30 |
31 |
32 | 38 |
39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /src/app/authentication/login/login.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, FormBuilder, Validators } from '@angular/forms'; 3 | import { AuthService } from 'src/app/auth/auth.service'; 4 | import { Router, ActivatedRoute } from '@angular/router'; 5 | import { MenuController, NavController } from '@ionic/angular'; 6 | 7 | @Component({ 8 | selector: 'app-login', 9 | templateUrl: './login.page.html', 10 | styleUrls: ['./login.page.scss'] 11 | }) 12 | export class LoginPage implements OnInit { 13 | form: FormGroup; 14 | 15 | constructor( 16 | private authService: AuthService, 17 | private router: Router, 18 | private route: ActivatedRoute, 19 | private fb: FormBuilder, 20 | public menuCtrl: MenuController, 21 | private nav: NavController 22 | ) {} 23 | 24 | ngOnInit() { 25 | this.form = this.fb.group({ 26 | email: ['', Validators.required], 27 | password: ['', Validators.required] 28 | }); 29 | } 30 | 31 | submit(form: any) { 32 | const { email, password } = form.value; 33 | this.authService.login(email, password); 34 | } 35 | 36 | onForgotPassword() { 37 | this.nav.navigateRoot(['authentication', 'forgot-password']); 38 | } 39 | 40 | onRegister() { 41 | this.nav.navigateRoot(['authentication', 'register']); 42 | } 43 | 44 | /* 45 | Disable sidebar 46 | https://stackoverflow.com/questions/51610075/disable-menu-on-login-page-ionic-4/51637860#51637860 47 | */ 48 | ionViewWillEnter() { 49 | this.menuCtrl.enable(false); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { Platform, NavController } from '@ionic/angular'; 4 | import { SplashScreen } from '@ionic-native/splash-screen/ngx'; 5 | import { StatusBar } from '@ionic-native/status-bar/ngx'; 6 | import { AngularFireAuth } from '@angular/fire/auth'; 7 | import { Router } from '@angular/router'; 8 | import { AuthService } from './auth/auth.service'; 9 | 10 | @Component({ 11 | selector: 'app-root', 12 | templateUrl: 'app.component.html' 13 | }) 14 | export class AppComponent { 15 | public appPages = [ 16 | { 17 | title: 'Home', 18 | url: '/pages/map', 19 | icon: 'home' 20 | }, 21 | { 22 | title: 'Profile', 23 | url: '/pages/profile', 24 | icon: 'person' 25 | }, 26 | { 27 | title: 'Notifications', 28 | url: '/pages/notifications', 29 | icon: 'notifications' 30 | } 31 | ]; 32 | 33 | constructor( 34 | private platform: Platform, 35 | private splashScreen: SplashScreen, 36 | private statusBar: StatusBar, 37 | private firebaseAuth: AngularFireAuth, 38 | private router: Router, 39 | private authService: AuthService, 40 | private nav: NavController 41 | ) { 42 | this.initializeApp(); 43 | } 44 | 45 | initializeApp() { 46 | this.platform.ready().then(() => { 47 | this.statusBar.styleDefault(); 48 | this.splashScreen.hide(); 49 | this.firebaseAuth.authState.subscribe(firebaseUser => { 50 | if (firebaseUser) { 51 | // this.router.navigate(['']); 52 | // this.router.navigate(['pages']); 53 | this.nav.navigateForward(['pages']); 54 | } 55 | }); 56 | }); 57 | } 58 | 59 | logout() { 60 | this.authService.logout(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lightning Alert 2 | 3 | Mobile app part of the project to warn farmers about lightning strikes, developed at [Smart India Hackathon 2019](https://sih.gov.in/sih2019). 4 | 5 | 6 | HOWTO 7 | ===== 8 | 9 | Test on browser 10 | --------------- 11 | 12 | - Step 1: `cd` into this folder and run: 13 | 14 | npm install 15 | 16 | - Step 2: Install cordova globally 17 | 18 | npm install --global ionic 19 | 20 | - Step 3: Run the app on browser 21 | 22 | ionic serve 23 | 24 | 25 | Run on Android 26 | -------------- 27 | 28 | - Step 1: Install cordova globally 29 | 30 | npm install --global cordova 31 | 32 | - Step 2: Run the app on browser or your preferred platform 33 | 34 | ionic cordova run android 35 | 36 | 37 | CREDITS 38 | ======= 39 | Developed by Team HyperCube 40 | --------------------------- 41 | - Gishan Abeysinhe 42 | - Rohan Villoth 43 | - Aswin A A 44 | - Agnes A 45 | - Adithyan K 46 | - Akshaya Karichery 47 | 48 | App icon 49 | -------- 50 | Derived from [Lightning by kareemovic](https://thenounproject.com/term/lightning/1461745/) licensed under [CC BY](https://creativecommons.org/licenses/by/3.0/us/legalcode) 51 | 52 | Login page background 53 | --------------------- 54 | Derived from [What The Hex Dark](https://toptal.com/designers/subtlepatterns/what-the-hex-dark/) by [Angel Micevski](https://behance.net/micevskiangel) 55 | 56 | Android icons 57 | ------------- 58 | Generated using [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html) 59 | 60 | Splash screens and iOS icons 61 | ---------------------------- 62 | Generated using [PhoneGap/Cordova Resource Generator](https://resource-generator.com) 63 | 64 | 65 | LICENSE 66 | ======= 67 | Except where otherwise noted, all original content here are released under [MIT license](https://opensource.org/licenses/MIT). However, part of the work here are derived from other open source projects and as a result follow the licenses of their original sources. 68 | -------------------------------------------------------------------------------- /src/app/authentication/login/login.page.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 9 |
10 |
11 | 12 | 18 | 19 | 20 | 26 | 27 |
28 |
29 | Forgot password? 32 |
33 |
34 | 43 |
44 |
45 | 51 |
52 |
53 |
54 |
55 | -------------------------------------------------------------------------------- /src/app/pages/profile/profile.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { MenuController } from '@ionic/angular'; 4 | 5 | import { Observable } from 'rxjs'; 6 | import { AngularFirestore, CollectionReference } from '@angular/fire/firestore'; 7 | import { AuthService } from 'src/app/auth/auth.service'; 8 | 9 | @Component({ 10 | selector: 'app-profile', 11 | templateUrl: './profile.page.html', 12 | styleUrls: ['./profile.page.scss'] 13 | }) 14 | export class ProfilePage implements OnInit { 15 | items: Observable; 16 | relatives: any; 17 | _profile: any; 18 | _relatives: any; 19 | 20 | constructor( 21 | private afs: AngularFirestore, 22 | private auth: AuthService, 23 | public menuCtrl: MenuController 24 | ) { 25 | // this.itemsCollection = afs.collection('Profiles').doc(); 26 | // this.items = this.itemsCollection.valueChanges(); 27 | } 28 | 29 | ngOnInit() { 30 | this.auth.uid.subscribe(uid => { 31 | console.log('uid', uid); 32 | this._profile = this.afs.collection('Profiles').doc(uid); 33 | this._relatives = this.afs 34 | .collection('Profiles') 35 | .doc(uid) 36 | .collection('Relatives'); 37 | 38 | this._profile.valueChanges().subscribe(data => { 39 | console.log('profile data', data); 40 | }); 41 | 42 | this._relatives.valueChanges().subscribe(data => { 43 | console.log('relatives data', data); 44 | this.relatives = data; 45 | }); 46 | }); 47 | } 48 | 49 | addContact(name: string, mobile: string) { 50 | if (name && mobile) { 51 | this._relatives.add({ 52 | name, 53 | mobile 54 | }); 55 | } else { 56 | alert('please add valid data'); 57 | } 58 | } 59 | 60 | updateContact(update_name: string, update_mobile: string) {} 61 | /* 62 | Disable sidebar 63 | https://stackoverflow.com/questions/51610075/disable-menu-on-login-page-ionic-4/51637860#51637860 64 | */ 65 | ionViewWillEnter() { 66 | this.menuCtrl.enable(false); 67 | } 68 | } 69 | 70 | export interface Profile { 71 | uid: string; 72 | name: string; 73 | relatives: CollectionReference; 74 | } 75 | -------------------------------------------------------------------------------- /src/app/authentication/register/register.page.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 9 |
10 |
11 | 19 | 20 | 26 | 27 | 28 | 34 | 35 | 36 | 42 | 43 |
44 |
45 | 54 |
55 |
56 | 62 |
63 |
64 |
65 |
66 | -------------------------------------------------------------------------------- /src/theme/variables.scss: -------------------------------------------------------------------------------- 1 | // Ionic Variables and Theming. For more info, please see: 2 | // http://ionicframework.com/docs/theming/ 3 | 4 | /** Ionic CSS Variables **/ 5 | :root { 6 | /** primary **/ 7 | --ion-color-primary: #3880ff; 8 | --ion-color-primary-rgb: 56, 128, 255; 9 | --ion-color-primary-contrast: #ffffff; 10 | --ion-color-primary-contrast-rgb: 255, 255, 255; 11 | --ion-color-primary-shade: #3171e0; 12 | --ion-color-primary-tint: #4c8dff; 13 | 14 | /** secondary **/ 15 | --ion-color-secondary: #0cd1e8; 16 | --ion-color-secondary-rgb: 12, 209, 232; 17 | --ion-color-secondary-contrast: #ffffff; 18 | --ion-color-secondary-contrast-rgb: 255, 255, 255; 19 | --ion-color-secondary-shade: #0bb8cc; 20 | --ion-color-secondary-tint: #24d6ea; 21 | 22 | /** tertiary **/ 23 | --ion-color-tertiary: #7044ff; 24 | --ion-color-tertiary-rgb: 112, 68, 255; 25 | --ion-color-tertiary-contrast: #ffffff; 26 | --ion-color-tertiary-contrast-rgb: 255, 255, 255; 27 | --ion-color-tertiary-shade: #633ce0; 28 | --ion-color-tertiary-tint: #7e57ff; 29 | 30 | /** success **/ 31 | --ion-color-success: #10dc60; 32 | --ion-color-success-rgb: 16, 220, 96; 33 | --ion-color-success-contrast: #ffffff; 34 | --ion-color-success-contrast-rgb: 255, 255, 255; 35 | --ion-color-success-shade: #0ec254; 36 | --ion-color-success-tint: #28e070; 37 | 38 | /** warning **/ 39 | --ion-color-warning: #ffce00; 40 | --ion-color-warning-rgb: 255, 206, 0; 41 | --ion-color-warning-contrast: #ffffff; 42 | --ion-color-warning-contrast-rgb: 255, 255, 255; 43 | --ion-color-warning-shade: #e0b500; 44 | --ion-color-warning-tint: #ffd31a; 45 | 46 | /** danger **/ 47 | --ion-color-danger: #f04141; 48 | --ion-color-danger-rgb: 245, 61, 61; 49 | --ion-color-danger-contrast: #ffffff; 50 | --ion-color-danger-contrast-rgb: 255, 255, 255; 51 | --ion-color-danger-shade: #d33939; 52 | --ion-color-danger-tint: #f25454; 53 | 54 | /** dark **/ 55 | --ion-color-dark: #222428; 56 | --ion-color-dark-rgb: 34, 34, 34; 57 | --ion-color-dark-contrast: #ffffff; 58 | --ion-color-dark-contrast-rgb: 255, 255, 255; 59 | --ion-color-dark-shade: #1e2023; 60 | --ion-color-dark-tint: #383a3e; 61 | 62 | /** medium **/ 63 | --ion-color-medium: #989aa2; 64 | --ion-color-medium-rgb: 152, 154, 162; 65 | --ion-color-medium-contrast: #ffffff; 66 | --ion-color-medium-contrast-rgb: 255, 255, 255; 67 | --ion-color-medium-shade: #86888f; 68 | --ion-color-medium-tint: #a2a4ab; 69 | 70 | /** light **/ 71 | --ion-color-light: #f4f5f8; 72 | --ion-color-light-rgb: 244, 244, 244; 73 | --ion-color-light-contrast: #000000; 74 | --ion-color-light-contrast-rgb: 0, 0, 0; 75 | --ion-color-light-shade: #d7d8da; 76 | --ion-color-light-tint: #f5f6f9; 77 | } 78 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { TestBed, async } from '@angular/core/testing'; 3 | 4 | import { Platform } from '@ionic/angular'; 5 | import { SplashScreen } from '@ionic-native/splash-screen/ngx'; 6 | import { StatusBar } from '@ionic-native/status-bar/ngx'; 7 | import { RouterTestingModule } from '@angular/router/testing'; 8 | 9 | import { AppComponent } from './app.component'; 10 | 11 | describe('AppComponent', () => { 12 | 13 | let statusBarSpy, splashScreenSpy, platformReadySpy, platformSpy; 14 | 15 | beforeEach(async(() => { 16 | statusBarSpy = jasmine.createSpyObj('StatusBar', ['styleDefault']); 17 | splashScreenSpy = jasmine.createSpyObj('SplashScreen', ['hide']); 18 | platformReadySpy = Promise.resolve(); 19 | platformSpy = jasmine.createSpyObj('Platform', { ready: platformReadySpy }); 20 | 21 | TestBed.configureTestingModule({ 22 | declarations: [AppComponent], 23 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 24 | providers: [ 25 | { provide: StatusBar, useValue: statusBarSpy }, 26 | { provide: SplashScreen, useValue: splashScreenSpy }, 27 | { provide: Platform, useValue: platformSpy }, 28 | ], 29 | imports: [ RouterTestingModule.withRoutes([])], 30 | }).compileComponents(); 31 | })); 32 | 33 | it('should create the app', async () => { 34 | const fixture = TestBed.createComponent(AppComponent); 35 | const app = fixture.debugElement.componentInstance; 36 | expect(app).toBeTruthy(); 37 | }); 38 | 39 | it('should initialize the app', async () => { 40 | TestBed.createComponent(AppComponent); 41 | expect(platformSpy.ready).toHaveBeenCalled(); 42 | await platformReadySpy; 43 | expect(statusBarSpy.styleDefault).toHaveBeenCalled(); 44 | expect(splashScreenSpy.hide).toHaveBeenCalled(); 45 | }); 46 | 47 | it('should have menu labels', async () => { 48 | const fixture = await TestBed.createComponent(AppComponent); 49 | await fixture.detectChanges(); 50 | const app = fixture.nativeElement; 51 | const menuItems = app.querySelectorAll('ion-label'); 52 | expect(menuItems.length).toEqual(2); 53 | expect(menuItems[0].textContent).toContain('Home'); 54 | expect(menuItems[1].textContent).toContain('List'); 55 | }); 56 | 57 | it('should have urls', async () => { 58 | const fixture = await TestBed.createComponent(AppComponent); 59 | await fixture.detectChanges(); 60 | const app = fixture.nativeElement; 61 | const menuItems = app.querySelectorAll('ion-item'); 62 | expect(menuItems.length).toEqual(2); 63 | expect(menuItems[0].getAttribute('ng-reflect-router-link')).toEqual('/home'); 64 | expect(menuItems[1].getAttribute('ng-reflect-router-link')).toEqual('/list'); 65 | }); 66 | 67 | }); 68 | -------------------------------------------------------------------------------- /src/app/pages/map/map.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; 2 | import { NavController, MenuController } from '@ionic/angular'; 3 | import leaflet from 'leaflet'; 4 | import { WeatherService } from 'src/app/services/weather.service'; 5 | import { MlService } from 'src/app/services/ml.service'; 6 | 7 | @Component({ 8 | selector: 'app-map', 9 | templateUrl: './map.page.html', 10 | styleUrls: ['./map.page.scss'] 11 | }) 12 | export class MapPage implements OnInit { 13 | @ViewChild('map') mapContainer: ElementRef; 14 | map: any; 15 | constructor( 16 | private nav: NavController, 17 | public menuCtrl: MenuController, 18 | private weather: WeatherService, 19 | private ml: MlService 20 | ) {} 21 | 22 | ngOnInit() { 23 | this.ml.getPrediction(); 24 | } 25 | 26 | /* 27 | Re-enable sidebar (It was disabled in login page) 28 | https://ionicframework.com/docs/api/menu 29 | */ 30 | ionViewWillEnter() { 31 | this.menuCtrl.enable(true, 'side-drawer'); 32 | } 33 | 34 | openSideDrawer() { 35 | this.menuCtrl.open('side-drawer'); 36 | } 37 | 38 | ionViewDidEnter() { 39 | this.loadmap(); 40 | } 41 | 42 | loadmap() { 43 | this.map = leaflet.map('map').fitWorld(); 44 | leaflet 45 | .tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 46 | attributions: 47 | // tslint:disable-next-line:max-line-length 48 | 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', 49 | maxZoom: 18 50 | }) 51 | .addTo(this.map); 52 | this.map 53 | .locate({ 54 | setView: true, 55 | maxZoom: 10 56 | }) 57 | .on('locationfound', e => { 58 | const markerGroup = leaflet.featureGroup(); 59 | const marker: any = leaflet 60 | .marker([e.latitude, e.longitude]) 61 | .on('click', () => { 62 | alert('Marker clicked'); 63 | }); 64 | const circle: any = leaflet 65 | .circle([e.latitude, e.longitude], 25000) 66 | .on('click', () => { 67 | alert('Marker clicked'); 68 | }); 69 | const circle2: any = leaflet 70 | .circle([e.latitude, e.longitude], 10000) 71 | .on('click', () => { 72 | alert('Marker clicked'); 73 | }); 74 | markerGroup.addLayer(marker); 75 | markerGroup.addLayer(circle); 76 | markerGroup.addLayer(circle2); 77 | this.map.addLayer(markerGroup); 78 | 79 | // this.weather.getCurrentWeatherDetails(e.latitude, e.longitude); 80 | }) 81 | .on('locationerror', err => { 82 | alert(err.message); 83 | }); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/app/services/cast.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class CastService { 8 | private castSession; 9 | private cast; 10 | public status = { 11 | casting: false 12 | }; 13 | 14 | constructor() {} 15 | 16 | initializeCastApi() { 17 | this.cast = window['chrome'].cast; 18 | const sessionRequest = new this.cast.SessionRequest( 19 | this.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID 20 | ); 21 | const apiConfig = new this.cast.ApiConfig( 22 | sessionRequest, 23 | s => {}, 24 | status => { 25 | if (status === this.cast.ReceiverAvailability.AVAILABLE) { 26 | } 27 | } 28 | ); 29 | const x = this.cast.initialize(apiConfig, this.onInitSuccess, this.onError); 30 | } 31 | 32 | onInitSuccess = function(e) { 33 | console.log('GCast initialization success'); 34 | }; 35 | 36 | onError = function(err) { 37 | console.log('GCast initialization failed', err); 38 | }; 39 | 40 | discoverDevices = function() { 41 | const self = this; 42 | const subj = new Subject(); 43 | this.cast.requestSession( 44 | function(s) { 45 | self.session = s; 46 | self.setCasting(true); 47 | subj.next('CONNECTED'); 48 | }, 49 | function(err) { 50 | self.setCasting(false); 51 | if (err.code === 'cancel') { 52 | self.session = undefined; 53 | subj.next('CANCEL'); 54 | } else { 55 | console.error('Error selecting a cast device', err); 56 | } 57 | } 58 | ); 59 | return subj; 60 | }; 61 | 62 | launchMedia = function(media) { 63 | const mediaInfo = new this.cast.media.MediaInfo(media); 64 | const request = new this.cast.media.LoadRequest(mediaInfo); 65 | console.log('launch media with session', this.session); 66 | 67 | if (!this.session) { 68 | window.open(media); 69 | return false; 70 | } 71 | this.session.loadMedia( 72 | request, 73 | this.onMediaDiscovered.bind(this, 'loadMedia'), 74 | this.onMediaError 75 | ); 76 | return true; 77 | }; 78 | 79 | onMediaDiscovered = function(how, media) { 80 | this.currentMedia = media; 81 | }; 82 | 83 | play = function() { 84 | this.currentMedia.play(null); 85 | }; 86 | 87 | pause = function() { 88 | this.currentMedia.pause(null); 89 | }; 90 | 91 | stop = function() { 92 | this.currentMedia.stop(null); 93 | }; 94 | 95 | onMediaError = function(err) { 96 | console.error('Error launching media', err); 97 | }; 98 | 99 | setCasting(value) { 100 | this.status.casting = value; 101 | } 102 | 103 | getStatus() { 104 | return this.status; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lightning-alert", 3 | "version": "0.0.1", 4 | "author": "Ionic Framework", 5 | "homepage": "http://ionicframework.com/", 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "build": "ng build", 10 | "test": "ng test", 11 | "lint": "ng lint", 12 | "e2e": "ng e2e" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/common": "~7.1.4", 17 | "@angular/core": "~7.1.4", 18 | "@angular/fire": "^5.1.1", 19 | "@angular/forms": "~7.1.4", 20 | "@angular/http": "~7.1.4", 21 | "@angular/platform-browser": "~7.1.4", 22 | "@angular/platform-browser-dynamic": "~7.1.4", 23 | "@angular/pwa": "^0.11.4", 24 | "@angular/router": "~7.1.4", 25 | "@angular/service-worker": "~7.1.4", 26 | "@ionic-native/core": "^5.0.0", 27 | "@ionic-native/google-maps": "^5.0.0-beta.27", 28 | "@ionic-native/splash-screen": "5.0.0-beta.21", 29 | "@ionic-native/status-bar": "5.0.0-beta.21", 30 | "@ionic/angular": "4.0.0-rc.0", 31 | "Chromecast": "git+https://github.com/nbabanov/cordova-chromecast.git", 32 | "cordova-android": "7.1.4", 33 | "cordova-browser": "5.0.4", 34 | "cordova-plugin-device": "^2.0.2", 35 | "cordova-plugin-geolocation": "^4.0.1", 36 | "cordova-plugin-ionic-keyboard": "^2.1.3", 37 | "cordova-plugin-ionic-webview": "^2.4.0", 38 | "cordova-plugin-splashscreen": "^5.0.2", 39 | "cordova-plugin-statusbar": "^2.4.2", 40 | "cordova-plugin-whitelist": "^1.3.3", 41 | "core-js": "^2.5.4", 42 | "darkskyjs": "^0.2.1", 43 | "firebase": "^5.7.2", 44 | "leaflet": "^1.4.0", 45 | "rxjs": "~6.3.3", 46 | "zone.js": "~0.8.26" 47 | }, 48 | "devDependencies": { 49 | "@angular-devkit/architect": "~0.11.4", 50 | "@angular-devkit/build-angular": "^0.13.4", 51 | "@angular-devkit/core": "~7.1.4", 52 | "@angular-devkit/schematics": "~7.1.4", 53 | "@angular/cli": "~7.1.4", 54 | "@angular/compiler": "~7.1.4", 55 | "@angular/compiler-cli": "^7.2.7", 56 | "@angular/language-service": "~7.1.4", 57 | "@ionic/angular-toolkit": "~1.2.0", 58 | "@types/jasmine": "~2.8.8", 59 | "@types/jasminewd2": "~2.0.3", 60 | "@types/node": "~10.12.0", 61 | "codelyzer": "~4.5.0", 62 | "jasmine-core": "~2.99.1", 63 | "jasmine-spec-reporter": "~4.2.1", 64 | "karma": "~3.1.4", 65 | "karma-chrome-launcher": "~2.2.0", 66 | "karma-coverage-istanbul-reporter": "~2.0.1", 67 | "karma-jasmine": "~1.1.2", 68 | "karma-jasmine-html-reporter": "^0.2.2", 69 | "protractor": "~5.4.0", 70 | "ts-node": "~7.0.0", 71 | "tslint": "~5.12.0", 72 | "typescript": "~3.1.6" 73 | }, 74 | "description": "An Ionic project", 75 | "cordova": { 76 | "plugins": { 77 | "cordova-plugin-whitelist": {}, 78 | "cordova-plugin-statusbar": {}, 79 | "cordova-plugin-device": {}, 80 | "cordova-plugin-splashscreen": {}, 81 | "cordova-plugin-ionic-webview": { 82 | "ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+" 83 | }, 84 | "cordova-plugin-ionic-keyboard": {}, 85 | "cordova-plugin-geolocation": { 86 | "GEOLOCATION_USAGE_DESCRIPTION": "To locate you" 87 | } 88 | }, 89 | "platforms": [ 90 | "browser", 91 | "android" 92 | ] 93 | } 94 | } -------------------------------------------------------------------------------- /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 | "use-input-property-decorator": true, 130 | "use-output-property-decorator": true, 131 | "use-host-property-decorator": true, 132 | "no-input-rename": true, 133 | "no-output-rename": true, 134 | "use-life-cycle-interface": true, 135 | "use-pipe-transform-interface": true, 136 | "directive-class-suffix": true 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/app/authentication/register/register.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { finalize } from 'rxjs/operators'; 4 | import { 5 | FormGroup, 6 | FormBuilder, 7 | Validators, 8 | AbstractControl 9 | } from '@angular/forms'; 10 | import { AuthService } from 'src/app/auth/auth.service'; 11 | import { Router, ActivatedRoute } from '@angular/router'; 12 | import { AngularFirestore } from '@angular/fire/firestore'; 13 | import { AngularFireStorage } from '@angular/fire/storage'; 14 | import { NavController } from '@ionic/angular'; 15 | 16 | @Component({ 17 | selector: 'app-register', 18 | templateUrl: './register.page.html', 19 | styleUrls: ['./register.page.scss'] 20 | }) 21 | export class RegisterPage implements OnInit { 22 | downloadURL: Observable; 23 | uploadPercent: Observable; 24 | profilePicdataURL: any; 25 | errorMessage: any; 26 | form: FormGroup; 27 | fileToUpload: File = null; 28 | constructor( 29 | private fb: FormBuilder, 30 | private authService: AuthService, 31 | private router: Router, 32 | private af: AngularFirestore, 33 | private storage: AngularFireStorage, 34 | private route: ActivatedRoute, 35 | private nav: NavController 36 | ) {} 37 | 38 | ngOnInit() { 39 | this.form = this.fb.group( 40 | { 41 | // name: ['', Validators.required], 42 | // id: ['', Validators.required], 43 | email: ['', Validators.required], 44 | password: ['', Validators.required], 45 | confirmPassword: ['', Validators.required] 46 | }, 47 | { validator: PasswordValidation.MatchPassword } 48 | ); 49 | } 50 | 51 | onSubmit() { 52 | const { name, email, password, id } = this.form.value; 53 | 54 | this.authService 55 | .createUser(email, password) 56 | .then((value: any) => { 57 | // const { uid } = value; 58 | // this.addNewUser(email, name, id, uid); 59 | }) 60 | .catch(err => { 61 | this.errorMessage = err.message; 62 | setTimeout(() => { 63 | this.errorMessage = null; 64 | }, 4000); 65 | }); 66 | } 67 | 68 | addNewUser(email, id, name, uid) { 69 | this.af 70 | .collection('users') 71 | .doc(uid) 72 | .set({ 73 | email, 74 | id, 75 | name, 76 | uid 77 | }) 78 | .then(value => { 79 | this.nav.navigateRoot(['pages']); 80 | }); 81 | } 82 | 83 | uploadProfilePic(event) { 84 | const file = event.target.files[0]; 85 | const filePath = `/profile-${Date.now()}`; 86 | const ref = this.storage.ref(filePath); 87 | const task = ref.put(file); 88 | 89 | this.uploadPercent = task.percentageChanges(); 90 | // get notified when the download URL is available 91 | task 92 | .snapshotChanges() 93 | .pipe(finalize(() => (this.downloadURL = ref.getDownloadURL()))) 94 | .subscribe(); 95 | } 96 | 97 | onForgotPassword() { 98 | this.router.navigate(['forgotpassword'], { relativeTo: this.route.parent }); 99 | } 100 | 101 | onLogin() { 102 | this.nav.goBack(); 103 | } 104 | } 105 | 106 | export class PasswordValidation { 107 | static MatchPassword(AC: AbstractControl) { 108 | const password = AC.get('password').value; // to get value in input tag 109 | const confirmPassword = AC.get('confirmPassword').value; // to get value in input tag 110 | if (password !== confirmPassword) { 111 | AC.get('confirmPassword').setErrors({ MatchPassword: true }); 112 | } else { 113 | return null; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /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 | /** IE9, IE10, IE11, and older Chrome requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** 38 | * If the application will be indexed by Google Search, the following is required. 39 | * Googlebot uses a renderer based on Chrome 41. 40 | * https://developers.google.com/search/docs/guides/rendering 41 | **/ 42 | // import 'core-js/es6/array'; 43 | 44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 45 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 46 | 47 | /** IE10 and IE11 requires the following for the Reflect API. */ 48 | // import 'core-js/es6/reflect'; 49 | 50 | /** 51 | * Web Animations `@angular/platform-browser/animations` 52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 54 | **/ 55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 56 | 57 | /** 58 | * By default, zone.js will patch all possible macroTask and DomEvents 59 | * user can disable parts of macroTask/DomEvents patch by setting following flags 60 | * because those flags need to be set before `zone.js` being loaded, and webpack 61 | * will put import in the top of bundle, so user need to create a separate file 62 | * in this directory (for example: zone-flags.ts), and put the following flags 63 | * into that file, and then add the following code before importing zone.js. 64 | * import './zone-flags.ts'; 65 | * 66 | * The flags allowed in zone-flags.ts are listed here. 67 | * 68 | * The following flags will work for all browsers. 69 | * 70 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 71 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 72 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 73 | * 74 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 75 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 76 | * 77 | * (window as any).__Zone_enable_cross_context_check = true; 78 | * 79 | */ 80 | 81 | /*************************************************************************************************** 82 | * Zone JS is required by default for Angular itself. 83 | */ 84 | import 'zone.js/dist/zone'; // Included with Angular CLI. 85 | 86 | 87 | /*************************************************************************************************** 88 | * APPLICATION IMPORTS 89 | */ 90 | -------------------------------------------------------------------------------- /src/app/pages/profile/profile.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | Profile 12 | 19 | 20 | 21 | 22 | 23 | 24 |
Location
25 |
26 | 27 | 28 | 29 | 30 | Current: Chandigarh 31 | 32 | 33 | 34 | 35 | Manually select location 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
Family & Friends
46 |
47 | 48 | 49 |

50 | In case of high alert, additional SMSs will be sent to the contacts added 51 | here. If you're not directly reachable, your contacts can warn you. 52 |

53 |
54 | 55 | 56 | 57 | 58 | {{ relative.name }} 59 | 60 | 61 | 62 | 63 | {{ relative.mobile }} 64 | 65 | 66 | 67 | 68 | 69 | Edit 70 | 71 | 72 | 73 | Delete 74 | 75 | 76 | 77 | 78 | 79 | 80 | Edit Name 81 | 82 | 83 | 84 | 85 | Edit Phone 86 | 87 | 88 | 89 | 90 | 91 | 92 | Save 93 | 94 | 95 | 96 | Cancel 97 | 98 | 99 | 100 | 101 | 102 | 103 | Enter Name 104 | 105 | 106 | 107 | 108 | Enter Phone 109 | 110 | 111 | 112 | 113 | 114 | 115 | Add Contact 116 | 117 | 118 | 119 |
120 | -------------------------------------------------------------------------------- /src/app/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, of } from 'rxjs'; 3 | import { AngularFireAuth } from '@angular/fire/auth'; 4 | import { Router } from '@angular/router'; 5 | import { NavController, ToastController } from '@ionic/angular'; 6 | import { AngularFirestore } from '@angular/fire/firestore'; 7 | import { environment } from 'src/environments/environment'; 8 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 9 | 10 | @Injectable() 11 | export class AuthService { 12 | user = { isAdmin: false }; 13 | token: any; 14 | authenticated = false; 15 | uid: Observable; 16 | 17 | constructor( 18 | private firebaseAuth: AngularFireAuth, 19 | public router: Router, 20 | private nav: NavController, 21 | public toastController: ToastController, 22 | private firestore: AngularFirestore, 23 | private http: HttpClient 24 | ) { 25 | this.firebaseAuth.authState.subscribe(firebaseUser => { 26 | if (firebaseUser) { 27 | this.uid = of(firebaseUser.uid); 28 | // this.nav.navigateForward(['..', 'pages']); 29 | // this.nav.navigateRoot(['pages']); 30 | this.authenticated = true; 31 | } 32 | }); 33 | } 34 | 35 | checkPermission() { 36 | return of(this.authenticated); 37 | } 38 | 39 | isLoggedIn() { 40 | return of(this.authenticated); 41 | } 42 | 43 | login(email: string, password: string) { 44 | this.firebaseAuth.auth 45 | .signInWithEmailAndPassword(email, password) 46 | .then(value => { 47 | this.token = value; 48 | }) 49 | .then(() => { 50 | this.authenticated = true; 51 | this.nav.navigateForward(['pages']); 52 | }) 53 | .catch(err => { 54 | this.presentToast(err.message); 55 | }); 56 | } 57 | 58 | async presentToast(message: string) { 59 | const toast = await this.toastController.create({ 60 | message, 61 | duration: 2000 62 | }); 63 | toast.present(); 64 | } 65 | 66 | createUser(email, password) { 67 | // const credential = firebase.auth.EmailAuthProvider.credential(email, password); 68 | return this.firebaseAuth.auth 69 | .createUserWithEmailAndPassword(email, password) 70 | .then(data => { 71 | this.firestore 72 | .collection('Profiles') 73 | .doc(data.user.uid) 74 | .set({ 75 | uid: data.user.uid 76 | }); 77 | this.emailVerification(); 78 | }); 79 | } 80 | 81 | logout() { 82 | this.firebaseAuth.auth 83 | .signOut() 84 | .then(() => { 85 | this.token = null; 86 | this.authenticated = false; 87 | this.nav.navigateRoot(['authentication']); 88 | }) 89 | .catch(() => { 90 | console.log('error'); 91 | }); 92 | } 93 | 94 | getToken() { 95 | return this.token; 96 | } 97 | 98 | isAuthenticated() { 99 | // here you can check if user is authenticated or not through his token 100 | return this.authenticated; 101 | } 102 | 103 | async emailVerification() { 104 | try { 105 | await this.firebaseAuth.auth.currentUser.sendEmailVerification(); 106 | return console.log('sent Password Reset Email!'); 107 | } catch (error) { 108 | return console.log(error); 109 | } 110 | } 111 | 112 | async resetPassword(email: string) { 113 | try { 114 | await this.firebaseAuth.auth.sendPasswordResetEmail(email); 115 | return console.log('sent Password Reset Email!'); 116 | } catch (error) { 117 | return console.log(error); 118 | } 119 | } 120 | 121 | getHeaders() { 122 | const headers = new HttpHeaders(); 123 | headers.append('Content-Type', 'application/json'); 124 | return headers; 125 | } 126 | 127 | authThirdParties(uid) {} 128 | 129 | authSMSServer(client) { 130 | const options = { 131 | headers: this.getHeaders() 132 | }; 133 | 134 | console.log('client details in service', client); 135 | return this.http 136 | .post(`${environment.SMSServerUrl}/client`, { client }, options) 137 | .subscribe(value => { 138 | console.log('value', value); 139 | }); 140 | } 141 | 142 | authMLServer(client) { 143 | const options = { 144 | headers: this.getHeaders() 145 | }; 146 | 147 | console.log('client details in service', client); 148 | return this.http 149 | .post(`${environment.MLServerURL}/client`, { client }, options) 150 | .subscribe(value => { 151 | console.log('value', value); 152 | }); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json", 3 | "version": 1, 4 | "defaultProject": "lightning-alert", 5 | "newProjectRoot": "projects", 6 | "projects": { 7 | "app": { 8 | "root": "", 9 | "sourceRoot": "src", 10 | "projectType": "application", 11 | "prefix": "app", 12 | "schematics": {}, 13 | "architect": { 14 | "build": { 15 | "builder": "@angular-devkit/build-angular:browser", 16 | "options": { 17 | "outputPath": "www", 18 | "index": "src/index.html", 19 | "main": "src/main.ts", 20 | "polyfills": "src/polyfills.ts", 21 | "tsConfig": "src/tsconfig.app.json", 22 | "assets": [ 23 | { 24 | "glob": "**/*", 25 | "input": "src/assets", 26 | "output": "assets" 27 | }, 28 | "src/manifest.json" 29 | ], 30 | "styles": [ 31 | { 32 | "input": "src/theme/variables.scss" 33 | }, 34 | { 35 | "input": "src/global.scss" 36 | } 37 | ], 38 | "scripts": [] 39 | }, 40 | "configurations": { 41 | "production": { 42 | "fileReplacements": [ 43 | { 44 | "replace": "src/environments/environment.ts", 45 | "with": "src/environments/environment.prod.ts" 46 | } 47 | ], 48 | "optimization": true, 49 | "outputHashing": "all", 50 | "sourceMap": false, 51 | "extractCss": true, 52 | "namedChunks": false, 53 | "aot": true, 54 | "extractLicenses": true, 55 | "vendorChunk": false, 56 | "buildOptimizer": true, 57 | "budgets": [ 58 | { 59 | "type": "initial", 60 | "maximumWarning": "2mb", 61 | "maximumError": "5mb" 62 | } 63 | ], 64 | "serviceWorker": true 65 | }, 66 | "ci": { 67 | "progress": false 68 | } 69 | } 70 | }, 71 | "serve": { 72 | "builder": "@angular-devkit/build-angular:dev-server", 73 | "options": { 74 | "browserTarget": "app:build" 75 | }, 76 | "configurations": { 77 | "production": { 78 | "browserTarget": "app:build:production" 79 | }, 80 | "ci": { 81 | "progress": false 82 | } 83 | } 84 | }, 85 | "extract-i18n": { 86 | "builder": "@angular-devkit/build-angular:extract-i18n", 87 | "options": { 88 | "browserTarget": "app:build" 89 | } 90 | }, 91 | "test": { 92 | "builder": "@angular-devkit/build-angular:karma", 93 | "options": { 94 | "main": "src/test.ts", 95 | "polyfills": "src/polyfills.ts", 96 | "tsConfig": "src/tsconfig.spec.json", 97 | "karmaConfig": "src/karma.conf.js", 98 | "styles": [], 99 | "scripts": [], 100 | "assets": [ 101 | { 102 | "glob": "favicon.ico", 103 | "input": "src/", 104 | "output": "/" 105 | }, 106 | { 107 | "glob": "**/*", 108 | "input": "src/assets", 109 | "output": "/assets" 110 | }, 111 | "src/manifest.json" 112 | ] 113 | }, 114 | "configurations": { 115 | "ci": { 116 | "progress": false, 117 | "watch": false 118 | } 119 | } 120 | }, 121 | "lint": { 122 | "builder": "@angular-devkit/build-angular:tslint", 123 | "options": { 124 | "tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"], 125 | "exclude": ["**/node_modules/**"] 126 | } 127 | }, 128 | "ionic-cordova-build": { 129 | "builder": "@ionic/angular-toolkit:cordova-build", 130 | "options": { 131 | "browserTarget": "app:build" 132 | }, 133 | "configurations": { 134 | "production": { 135 | "browserTarget": "app:build:production" 136 | } 137 | } 138 | }, 139 | "ionic-cordova-serve": { 140 | "builder": "@ionic/angular-toolkit:cordova-serve", 141 | "options": { 142 | "cordovaBuildTarget": "app:ionic-cordova-build", 143 | "devServerTarget": "app:serve" 144 | }, 145 | "configurations": { 146 | "production": { 147 | "cordovaBuildTarget": "app:ionic-cordova-build:production", 148 | "devServerTarget": "app:serve:production" 149 | } 150 | } 151 | } 152 | } 153 | }, 154 | "app-e2e": { 155 | "root": "e2e/", 156 | "projectType": "application", 157 | "architect": { 158 | "e2e": { 159 | "builder": "@angular-devkit/build-angular:protractor", 160 | "options": { 161 | "protractorConfig": "e2e/protractor.conf.js", 162 | "devServerTarget": "app:serve" 163 | }, 164 | "configurations": { 165 | "ci": { 166 | "devServerTarget": "app:serve:ci" 167 | } 168 | } 169 | }, 170 | "lint": { 171 | "builder": "@angular-devkit/build-angular:tslint", 172 | "options": { 173 | "tsConfig": "e2e/tsconfig.e2e.json", 174 | "exclude": ["**/node_modules/**"] 175 | } 176 | } 177 | } 178 | } 179 | }, 180 | "cli": { 181 | "defaultCollection": "@ionic/angular-toolkit" 182 | }, 183 | "schematics": { 184 | "@ionic/angular-toolkit:component": { 185 | "styleext": "scss" 186 | }, 187 | "@ionic/angular-toolkit:page": { 188 | "styleext": "scss" 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Lightning Alert 4 | Lightning alert for farmers 5 | Team HyperCube 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/app/services/test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "time": 1551529800, 4 | "summary": "Breezy and Humid", 5 | "icon": "wind", 6 | "precipIntensity": 0.0007, 7 | "precipProbability": 0.03, 8 | "precipType": "rain", 9 | "temperature": 86.55, 10 | "apparentTemperature": 96.85, 11 | "dewPoint": 75.96, 12 | "humidity": 0.71, 13 | "pressure": 1008.5, 14 | "windSpeed": 16.7, 15 | "windGust": 16.79, 16 | "windBearing": 271, 17 | "cloudCover": 0.38, 18 | "uvIndex": 0, 19 | "visibility": 1.95, 20 | "ozone": 251.2 21 | }, 22 | { 23 | "time": 1551533400, 24 | "summary": "Humid and Foggy", 25 | "icon": "fog", 26 | "precipIntensity": 0.0037, 27 | "precipProbability": 0.06, 28 | "precipType": "rain", 29 | "temperature": 85.43, 30 | "apparentTemperature": 95.11, 31 | "dewPoint": 75.92, 32 | "humidity": 0.73, 33 | "pressure": 1009.33, 34 | "windSpeed": 14.2, 35 | "windGust": 15.93, 36 | "windBearing": 273, 37 | "cloudCover": 0.39, 38 | "uvIndex": 0, 39 | "visibility": 0, 40 | "ozone": 251.3 41 | }, 42 | { 43 | "time": 1551537000, 44 | "summary": "Humid and Foggy", 45 | "icon": "fog", 46 | "precipIntensity": 0.0096, 47 | "precipProbability": 0.1, 48 | "precipType": "rain", 49 | "temperature": 84.27, 50 | "apparentTemperature": 92.81, 51 | "dewPoint": 75.45, 52 | "humidity": 0.75, 53 | "pressure": 1010.19, 54 | "windSpeed": 10.46, 55 | "windGust": 15.31, 56 | "windBearing": 287, 57 | "cloudCover": 0.57, 58 | "uvIndex": 0, 59 | "visibility": 0.66, 60 | "ozone": 251.51 61 | }, 62 | { 63 | "time": 1551540600, 64 | "summary": "Humid and Mostly Cloudy", 65 | "icon": "partly-cloudy-night", 66 | "precipIntensity": 0.0148, 67 | "precipProbability": 0.13, 68 | "precipType": "rain", 69 | "temperature": 83.1, 70 | "apparentTemperature": 90.4, 71 | "dewPoint": 74.83, 72 | "humidity": 0.76, 73 | "pressure": 1010.89, 74 | "windSpeed": 7.55, 75 | "windGust": 14.69, 76 | "windBearing": 304, 77 | "cloudCover": 0.73, 78 | "uvIndex": 0, 79 | "visibility": 3.77, 80 | "ozone": 251.96 81 | }, 82 | { 83 | "time": 1551544200, 84 | "summary": "Humid and Mostly Cloudy", 85 | "icon": "partly-cloudy-night", 86 | "precipIntensity": 0.0181, 87 | "precipProbability": 0.16, 88 | "precipType": "rain", 89 | "temperature": 82.35, 90 | "apparentTemperature": 89.04, 91 | "dewPoint": 74.66, 92 | "humidity": 0.78, 93 | "pressure": 1011.4, 94 | "windSpeed": 6.51, 95 | "windGust": 14.05, 96 | "windBearing": 322, 97 | "cloudCover": 0.77, 98 | "uvIndex": 0, 99 | "visibility": 4.41, 100 | "ozone": 252.63 101 | }, 102 | { 103 | "time": 1551547800, 104 | "summary": "Humid and Mostly Cloudy", 105 | "icon": "partly-cloudy-night", 106 | "precipIntensity": 0.0214, 107 | "precipProbability": 0.18, 108 | "precipType": "rain", 109 | "temperature": 81.76, 110 | "apparentTemperature": 88.06, 111 | "dewPoint": 74.72, 112 | "humidity": 0.79, 113 | "pressure": 1011.62, 114 | "windSpeed": 6.27, 115 | "windGust": 13.17, 116 | "windBearing": 338, 117 | "cloudCover": 0.71, 118 | "uvIndex": 0, 119 | "visibility": 3.68, 120 | "ozone": 253.24 121 | }, 122 | { 123 | "time": 1551551400, 124 | "summary": "Humid and Partly Cloudy", 125 | "icon": "partly-cloudy-night", 126 | "precipIntensity": 0.0226, 127 | "precipProbability": 0.21, 128 | "precipType": "rain", 129 | "temperature": 81, 130 | "apparentTemperature": 86.64, 131 | "dewPoint": 74.63, 132 | "humidity": 0.81, 133 | "pressure": 1011.38, 134 | "windSpeed": 5.89, 135 | "windGust": 11.71, 136 | "windBearing": 351, 137 | "cloudCover": 0.57, 138 | "uvIndex": 0, 139 | "visibility": 4.11, 140 | "ozone": 253.37 141 | }, 142 | { 143 | "time": 1551555000, 144 | "summary": "Humid and Partly Cloudy", 145 | "icon": "partly-cloudy-night", 146 | "precipIntensity": 0.0199, 147 | "precipProbability": 0.22, 148 | "precipType": "rain", 149 | "temperature": 80.32, 150 | "apparentTemperature": 85.27, 151 | "dewPoint": 74.39, 152 | "humidity": 0.82, 153 | "pressure": 1010.75, 154 | "windSpeed": 5.08, 155 | "windGust": 9.67, 156 | "windBearing": 357, 157 | "cloudCover": 0.41, 158 | "uvIndex": 0, 159 | "visibility": 5.69, 160 | "ozone": 253.04 161 | }, 162 | { 163 | "time": 1551558600, 164 | "summary": "Humid and Partly Cloudy", 165 | "icon": "partly-cloudy-night", 166 | "precipIntensity": 0.0163, 167 | "precipProbability": 0.21, 168 | "precipType": "rain", 169 | "temperature": 79.85, 170 | "apparentTemperature": 84.26, 171 | "dewPoint": 74.14, 172 | "humidity": 0.83, 173 | "pressure": 1010.15, 174 | "windSpeed": 4.35, 175 | "windGust": 7.61, 176 | "windBearing": 3, 177 | "cloudCover": 0.38, 178 | "uvIndex": 0, 179 | "visibility": 7.46, 180 | "ozone": 252.63 181 | }, 182 | { 183 | "time": 1551562200, 184 | "summary": "Humid and Partly Cloudy", 185 | "icon": "partly-cloudy-night", 186 | "precipIntensity": 0.0127, 187 | "precipProbability": 0.17, 188 | "precipType": "rain", 189 | "temperature": 79.29, 190 | "apparentTemperature": 83.15, 191 | "dewPoint": 74.02, 192 | "humidity": 0.84, 193 | "pressure": 1009.81, 194 | "windSpeed": 4.16, 195 | "windGust": 6.13, 196 | "windBearing": 11, 197 | "cloudCover": 0.44, 198 | "uvIndex": 0, 199 | "visibility": 8.31, 200 | "ozone": 252.32 201 | }, 202 | { 203 | "time": 1551565800, 204 | "summary": "Humid and Partly Cloudy", 205 | "icon": "partly-cloudy-night", 206 | "precipIntensity": 0.0086, 207 | "precipProbability": 0.13, 208 | "precipType": "rain", 209 | "temperature": 79.23, 210 | "apparentTemperature": 83.02, 211 | "dewPoint": 74.05, 212 | "humidity": 0.84, 213 | "pressure": 1009.66, 214 | "windSpeed": 4.12, 215 | "windGust": 5.31, 216 | "windBearing": 14, 217 | "cloudCover": 0.47, 218 | "uvIndex": 0, 219 | "visibility": 8.22, 220 | "ozone": 252.14 221 | }, 222 | { 223 | "time": 1551569400, 224 | "summary": "Humid and Partly Cloudy", 225 | "icon": "partly-cloudy-night", 226 | "precipIntensity": 0.0046, 227 | "precipProbability": 0.09, 228 | "precipType": "rain", 229 | "temperature": 79.51, 230 | "apparentTemperature": 83.6, 231 | "dewPoint": 74.11, 232 | "humidity": 0.84, 233 | "pressure": 1009.72, 234 | "windSpeed": 4.17, 235 | "windGust": 4.85, 236 | "windBearing": 14, 237 | "cloudCover": 0.51, 238 | "uvIndex": 0, 239 | "visibility": 8.25, 240 | "ozone": 252.07 241 | }, 242 | { 243 | "time": 1551573000, 244 | "summary": "Humid and Partly Cloudy", 245 | "icon": "partly-cloudy-night", 246 | "precipIntensity": 0.002, 247 | "precipProbability": 0.06, 248 | "precipType": "rain", 249 | "temperature": 79.68, 250 | "apparentTemperature": 83.95, 251 | "dewPoint": 74.15, 252 | "humidity": 0.83, 253 | "pressure": 1010.09, 254 | "windSpeed": 4.5, 255 | "windGust": 4.78, 256 | "windBearing": 13, 257 | "cloudCover": 0.55, 258 | "uvIndex": 0, 259 | "visibility": 9.41, 260 | "ozone": 252.14 261 | }, 262 | { 263 | "time": 1551576600, 264 | "summary": "Humid and Partly Cloudy", 265 | "icon": "partly-cloudy-day", 266 | "precipIntensity": 0.001, 267 | "precipProbability": 0.04, 268 | "precipType": "rain", 269 | "temperature": 80.15, 270 | "apparentTemperature": 84.86, 271 | "dewPoint": 74.18, 272 | "humidity": 0.82, 273 | "pressure": 1010.72, 274 | "windSpeed": 4.92, 275 | "windGust": 5.08, 276 | "windBearing": 17, 277 | "cloudCover": 0.55, 278 | "uvIndex": 0, 279 | "visibility": 10, 280 | "ozone": 252.41 281 | }, 282 | { 283 | "time": 1551580200, 284 | "summary": "Humid and Partly Cloudy", 285 | "icon": "partly-cloudy-day", 286 | "precipIntensity": 0.0008, 287 | "precipProbability": 0.02, 288 | "precipType": "rain", 289 | "temperature": 81.11, 290 | "apparentTemperature": 86.59, 291 | "dewPoint": 74.16, 292 | "humidity": 0.8, 293 | "pressure": 1011.38, 294 | "windSpeed": 5.28, 295 | "windGust": 5.41, 296 | "windBearing": 24, 297 | "cloudCover": 0.51, 298 | "uvIndex": 1, 299 | "visibility": 10, 300 | "ozone": 252.72 301 | }, 302 | { 303 | "time": 1551583800, 304 | "summary": "Humid and Partly Cloudy", 305 | "icon": "partly-cloudy-day", 306 | "precipIntensity": 0.0006, 307 | "precipProbability": 0.01, 308 | "precipType": "rain", 309 | "temperature": 82.89, 310 | "apparentTemperature": 89.43, 311 | "dewPoint": 73.98, 312 | "humidity": 0.75, 313 | "pressure": 1011.78, 314 | "windSpeed": 4.97, 315 | "windGust": 5.17, 316 | "windBearing": 31, 317 | "cloudCover": 0.48, 318 | "uvIndex": 3, 319 | "visibility": 10, 320 | "ozone": 252.87 321 | }, 322 | { 323 | "time": 1551587400, 324 | "summary": "Humid and Partly Cloudy", 325 | "icon": "partly-cloudy-day", 326 | "precipIntensity": 0.0002, 327 | "precipProbability": 0.01, 328 | "precipType": "rain", 329 | "temperature": 85.47, 330 | "apparentTemperature": 92.98, 331 | "dewPoint": 73.65, 332 | "humidity": 0.68, 333 | "pressure": 1011.8, 334 | "windSpeed": 2.68, 335 | "windGust": 4.31, 336 | "windBearing": 34, 337 | "cloudCover": 0.45, 338 | "uvIndex": 6, 339 | "visibility": 10, 340 | "ozone": 252.9 341 | }, 342 | { 343 | "time": 1551591000, 344 | "summary": "Humid and Partly Cloudy", 345 | "icon": "partly-cloudy-day", 346 | "precipIntensity": 0, 347 | "precipProbability": 0, 348 | "temperature": 87.72, 349 | "apparentTemperature": 95.72, 350 | "dewPoint": 73.33, 351 | "humidity": 0.63, 352 | "pressure": 1011.43, 353 | "windSpeed": 1.66, 354 | "windGust": 4.1, 355 | "windBearing": 282, 356 | "cloudCover": 0.42, 357 | "uvIndex": 8, 358 | "visibility": 10, 359 | "ozone": 252.77 360 | }, 361 | { 362 | "time": 1551594600, 363 | "summary": "Humid and Partly Cloudy", 364 | "icon": "partly-cloudy-day", 365 | "precipIntensity": 0, 366 | "precipProbability": 0, 367 | "temperature": 88.61, 368 | "apparentTemperature": 96.76, 369 | "dewPoint": 73.19, 370 | "humidity": 0.61, 371 | "pressure": 1010.55, 372 | "windSpeed": 4.98, 373 | "windGust": 5.4, 374 | "windBearing": 271, 375 | "cloudCover": 0.33, 376 | "uvIndex": 10, 377 | "visibility": 10, 378 | "ozone": 252.17 379 | }, 380 | { 381 | "time": 1551598200, 382 | "summary": "Humid", 383 | "icon": "clear-day", 384 | "precipIntensity": 0, 385 | "precipProbability": 0, 386 | "temperature": 88.58, 387 | "apparentTemperature": 96.78, 388 | "dewPoint": 73.25, 389 | "humidity": 0.61, 390 | "pressure": 1009.33, 391 | "windSpeed": 7.56, 392 | "windGust": 8, 393 | "windBearing": 272, 394 | "cloudCover": 0.19, 395 | "uvIndex": 11, 396 | "visibility": 10, 397 | "ozone": 251.05 398 | }, 399 | { 400 | "time": 1551601800, 401 | "summary": "Humid", 402 | "icon": "clear-day", 403 | "precipIntensity": 0, 404 | "precipProbability": 0, 405 | "temperature": 88.48, 406 | "apparentTemperature": 96.77, 407 | "dewPoint": 73.37, 408 | "humidity": 0.61, 409 | "pressure": 1008.13, 410 | "windSpeed": 9.69, 411 | "windGust": 11.04, 412 | "windBearing": 267, 413 | "cloudCover": 0.06, 414 | "uvIndex": 11, 415 | "visibility": 10, 416 | "ozone": 249.84 417 | }, 418 | { 419 | "time": 1551605400, 420 | "summary": "Humid", 421 | "icon": "clear-day", 422 | "precipIntensity": 0.0003, 423 | "precipProbability": 0.02, 424 | "precipType": "rain", 425 | "temperature": 88.21, 426 | "apparentTemperature": 96.51, 427 | "dewPoint": 73.46, 428 | "humidity": 0.62, 429 | "pressure": 1007.37, 430 | "windSpeed": 10.98, 431 | "windGust": 13.38, 432 | "windBearing": 252, 433 | "cloudCover": 0, 434 | "uvIndex": 8, 435 | "visibility": 10, 436 | "ozone": 248.85 437 | }, 438 | { 439 | "time": 1551609000, 440 | "summary": "Humid", 441 | "icon": "clear-day", 442 | "precipIntensity": 0.0004, 443 | "precipProbability": 0.02, 444 | "precipType": "rain", 445 | "temperature": 87.56, 446 | "apparentTemperature": 95.71, 447 | "dewPoint": 73.53, 448 | "humidity": 0.63, 449 | "pressure": 1007.12, 450 | "windSpeed": 9.8, 451 | "windGust": 14.8, 452 | "windBearing": 280, 453 | "cloudCover": 0, 454 | "uvIndex": 4, 455 | "visibility": 10, 456 | "ozone": 248.06 457 | }, 458 | { 459 | "time": 1551612600, 460 | "summary": "Humid", 461 | "icon": "clear-day", 462 | "precipIntensity": 0.0004, 463 | "precipProbability": 0.02, 464 | "precipType": "rain", 465 | "temperature": 86.6, 466 | "apparentTemperature": 94.52, 467 | "dewPoint": 73.63, 468 | "humidity": 0.65, 469 | "pressure": 1007.22, 470 | "windSpeed": 11.95, 471 | "windGust": 15.64, 472 | "windBearing": 304, 473 | "cloudCover": 0.02, 474 | "uvIndex": 1, 475 | "visibility": 10, 476 | "ozone": 247.59 477 | }, 478 | { 479 | "time": 1551616200, 480 | "summary": "Humid", 481 | "icon": "clear-day", 482 | "precipIntensity": 0.0003, 483 | "precipProbability": 0.02, 484 | "precipType": "rain", 485 | "temperature": 85.46, 486 | "apparentTemperature": 93.11, 487 | "dewPoint": 73.81, 488 | "humidity": 0.68, 489 | "pressure": 1007.7, 490 | "windSpeed": 11.47, 491 | "windGust": 15.93, 492 | "windBearing": 295, 493 | "cloudCover": 0.1, 494 | "uvIndex": 0, 495 | "visibility": 10, 496 | "ozone": 247.59 497 | }, 498 | { 499 | "time": 1551619800, 500 | "summary": "Humid", 501 | "icon": "clear-night", 502 | "precipIntensity": 0, 503 | "precipProbability": 0, 504 | "temperature": 84.55, 505 | "apparentTemperature": 92.02, 506 | "dewPoint": 74.06, 507 | "humidity": 0.71, 508 | "pressure": 1008.52, 509 | "windSpeed": 10.37, 510 | "windGust": 15.67, 511 | "windBearing": 301, 512 | "cloudCover": 0.24, 513 | "uvIndex": 0, 514 | "visibility": 10, 515 | "ozone": 248 516 | }, 517 | { 518 | "time": 1551623400, 519 | "summary": "Humid and Partly Cloudy", 520 | "icon": "partly-cloudy-night", 521 | "precipIntensity": 0, 522 | "precipProbability": 0, 523 | "temperature": 83.89, 524 | "apparentTemperature": 91.24, 525 | "dewPoint": 74.32, 526 | "humidity": 0.73, 527 | "pressure": 1009.42, 528 | "windSpeed": 9.07, 529 | "windGust": 14.98, 530 | "windBearing": 313, 531 | "cloudCover": 0.34, 532 | "uvIndex": 0, 533 | "visibility": 10, 534 | "ozone": 248.6 535 | }, 536 | { 537 | "time": 1551627000, 538 | "summary": "Humid and Partly Cloudy", 539 | "icon": "partly-cloudy-night", 540 | "precipIntensity": 0, 541 | "precipProbability": 0, 542 | "temperature": 83.23, 543 | "apparentTemperature": 90.4, 544 | "dewPoint": 74.55, 545 | "humidity": 0.75, 546 | "pressure": 1010.09, 547 | "windSpeed": 7.89, 548 | "windGust": 13.9, 549 | "windBearing": 321, 550 | "cloudCover": 0.32, 551 | "uvIndex": 0, 552 | "visibility": 10, 553 | "ozone": 249.28 554 | }, 555 | { 556 | "time": 1551630600, 557 | "summary": "Humid and Partly Cloudy", 558 | "icon": "partly-cloudy-night", 559 | "precipIntensity": 0.0005, 560 | "precipProbability": 0.01, 561 | "precipType": "rain", 562 | "temperature": 82.61, 563 | "apparentTemperature": 89.53, 564 | "dewPoint": 74.75, 565 | "humidity": 0.77, 566 | "pressure": 1010.47, 567 | "windSpeed": 7.17, 568 | "windGust": 12.43, 569 | "windBearing": 327, 570 | "cloudCover": 0.28, 571 | "uvIndex": 0, 572 | "visibility": 10, 573 | "ozone": 249.94 574 | }, 575 | { 576 | "time": 1551634200, 577 | "summary": "Humid and Partly Cloudy", 578 | "icon": "partly-cloudy-night", 579 | "precipIntensity": 0.001, 580 | "precipProbability": 0.02, 581 | "precipType": "rain", 582 | "temperature": 82.04, 583 | "apparentTemperature": 88.65, 584 | "dewPoint": 74.88, 585 | "humidity": 0.79, 586 | "pressure": 1010.59, 587 | "windSpeed": 6.63, 588 | "windGust": 10.93, 589 | "windBearing": 331, 590 | "cloudCover": 0.28, 591 | "uvIndex": 0, 592 | "visibility": 10, 593 | "ozone": 250.47 594 | }, 595 | { 596 | "time": 1551637800, 597 | "summary": "Humid and Partly Cloudy", 598 | "icon": "partly-cloudy-night", 599 | "precipIntensity": 0.0011, 600 | "precipProbability": 0.02, 601 | "precipType": "rain", 602 | "temperature": 81.45, 603 | "apparentTemperature": 87.61, 604 | "dewPoint": 74.91, 605 | "humidity": 0.81, 606 | "pressure": 1010.37, 607 | "windSpeed": 6.03, 608 | "windGust": 9.82, 609 | "windBearing": 336, 610 | "cloudCover": 0.32, 611 | "uvIndex": 0, 612 | "visibility": 10, 613 | "ozone": 250.78 614 | }, 615 | { 616 | "time": 1551641400, 617 | "summary": "Humid and Partly Cloudy", 618 | "icon": "partly-cloudy-night", 619 | "precipIntensity": 0.0009, 620 | "precipProbability": 0.03, 621 | "precipType": "rain", 622 | "temperature": 80.96, 623 | "apparentTemperature": 86.68, 624 | "dewPoint": 74.84, 625 | "humidity": 0.82, 626 | "pressure": 1009.85, 627 | "windSpeed": 5.58, 628 | "windGust": 9.11, 629 | "windBearing": 343, 630 | "cloudCover": 0.34, 631 | "uvIndex": 0, 632 | "visibility": 10, 633 | "ozone": 250.76 634 | }, 635 | { 636 | "time": 1551645000, 637 | "summary": "Humid and Partly Cloudy", 638 | "icon": "partly-cloudy-night", 639 | "precipIntensity": 0.0009, 640 | "precipProbability": 0.03, 641 | "precipType": "rain", 642 | "temperature": 80.61, 643 | "apparentTemperature": 85.96, 644 | "dewPoint": 74.71, 645 | "humidity": 0.82, 646 | "pressure": 1009.25, 647 | "windSpeed": 5.28, 648 | "windGust": 8.46, 649 | "windBearing": 350, 650 | "cloudCover": 0.34, 651 | "uvIndex": 0, 652 | "visibility": 10, 653 | "ozone": 250.6 654 | }, 655 | { 656 | "time": 1551648600, 657 | "summary": "Humid and Partly Cloudy", 658 | "icon": "partly-cloudy-night", 659 | "precipIntensity": 0.0009, 660 | "precipProbability": 0.04, 661 | "precipType": "rain", 662 | "temperature": 80.05, 663 | "apparentTemperature": 84.81, 664 | "dewPoint": 74.57, 665 | "humidity": 0.83, 666 | "pressure": 1008.88, 667 | "windSpeed": 4.89, 668 | "windGust": 7.59, 669 | "windBearing": 0, 670 | "cloudCover": 0.34, 671 | "uvIndex": 0, 672 | "visibility": 10, 673 | "ozone": 250.33 674 | }, 675 | { 676 | "time": 1551652200, 677 | "summary": "Humid and Partly Cloudy", 678 | "icon": "partly-cloudy-night", 679 | "precipIntensity": 0.0008, 680 | "precipProbability": 0.03, 681 | "precipType": "rain", 682 | "temperature": 79.32, 683 | "apparentTemperature": 83.31, 684 | "dewPoint": 74.41, 685 | "humidity": 0.85, 686 | "pressure": 1008.78, 687 | "windSpeed": 4.5, 688 | "windGust": 6.48, 689 | "windBearing": 12, 690 | "cloudCover": 0.28, 691 | "uvIndex": 0, 692 | "visibility": 10, 693 | "ozone": 249.93 694 | }, 695 | { 696 | "time": 1551655800, 697 | "summary": "Humid and Partly Cloudy", 698 | "icon": "partly-cloudy-night", 699 | "precipIntensity": 0.0007, 700 | "precipProbability": 0.03, 701 | "precipType": "rain", 702 | "temperature": 78.79, 703 | "apparentTemperature": 82.18, 704 | "dewPoint": 74.23, 705 | "humidity": 0.86, 706 | "pressure": 1008.91, 707 | "windSpeed": 4.3, 708 | "windGust": 5.58, 709 | "windBearing": 26, 710 | "cloudCover": 0.25, 711 | "uvIndex": 0, 712 | "visibility": 10, 713 | "ozone": 249.58 714 | }, 715 | { 716 | "time": 1551659400, 717 | "summary": "Humid and Partly Cloudy", 718 | "icon": "partly-cloudy-night", 719 | "precipIntensity": 0.0009, 720 | "precipProbability": 0.03, 721 | "precipType": "rain", 722 | "temperature": 78.77, 723 | "apparentTemperature": 82.1, 724 | "dewPoint": 74.08, 725 | "humidity": 0.86, 726 | "pressure": 1009.43, 727 | "windSpeed": 4.35, 728 | "windGust": 5.2, 729 | "windBearing": 37, 730 | "cloudCover": 0.32, 731 | "uvIndex": 0, 732 | "visibility": 10, 733 | "ozone": 249.44 734 | }, 735 | { 736 | "time": 1551663000, 737 | "summary": "Humid and Partly Cloudy", 738 | "icon": "partly-cloudy-day", 739 | "precipIntensity": 0.0009, 740 | "precipProbability": 0.02, 741 | "precipType": "rain", 742 | "temperature": 79.43, 743 | "apparentTemperature": 83.39, 744 | "dewPoint": 73.92, 745 | "humidity": 0.83, 746 | "pressure": 1010.27, 747 | "windSpeed": 4.49, 748 | "windGust": 5.21, 749 | "windBearing": 45, 750 | "cloudCover": 0.37, 751 | "uvIndex": 0, 752 | "visibility": 10, 753 | "ozone": 249.51 754 | }, 755 | { 756 | "time": 1551666600, 757 | "summary": "Humid and Partly Cloudy", 758 | "icon": "partly-cloudy-day", 759 | "precipIntensity": 0.0005, 760 | "precipProbability": 0.02, 761 | "precipType": "rain", 762 | "temperature": 80.94, 763 | "apparentTemperature": 86.11, 764 | "dewPoint": 73.79, 765 | "humidity": 0.79, 766 | "pressure": 1011.09, 767 | "windSpeed": 4.53, 768 | "windGust": 5.17, 769 | "windBearing": 48, 770 | "cloudCover": 0.34, 771 | "uvIndex": 1, 772 | "visibility": 10, 773 | "ozone": 249.62 774 | }, 775 | { 776 | "time": 1551670200, 777 | "summary": "Humid and Partly Cloudy", 778 | "icon": "partly-cloudy-day", 779 | "precipIntensity": 0.0002, 780 | "precipProbability": 0.01, 781 | "precipType": "rain", 782 | "temperature": 83.21, 783 | "apparentTemperature": 89.75, 784 | "dewPoint": 73.74, 785 | "humidity": 0.73, 786 | "pressure": 1011.6, 787 | "windSpeed": 3.94, 788 | "windGust": 4.56, 789 | "windBearing": 36, 790 | "cloudCover": 0.28, 791 | "uvIndex": 3, 792 | "visibility": 10, 793 | "ozone": 249.62 794 | }, 795 | { 796 | "time": 1551673800, 797 | "summary": "Humid and Partly Cloudy", 798 | "icon": "partly-cloudy-day", 799 | "precipIntensity": 0, 800 | "precipProbability": 0, 801 | "temperature": 85.77, 802 | "apparentTemperature": 93.49, 803 | "dewPoint": 73.74, 804 | "humidity": 0.68, 805 | "pressure": 1011.7, 806 | "windSpeed": 2.11, 807 | "windGust": 3.62, 808 | "windBearing": 340, 809 | "cloudCover": 0.25, 810 | "uvIndex": 6, 811 | "visibility": 10, 812 | "ozone": 249.49 813 | }, 814 | { 815 | "time": 1551677400, 816 | "summary": "Humid", 817 | "icon": "clear-day", 818 | "precipIntensity": 0, 819 | "precipProbability": 0, 820 | "temperature": 87.69, 821 | "apparentTemperature": 96.18, 822 | "dewPoint": 73.82, 823 | "humidity": 0.64, 824 | "pressure": 1011.36, 825 | "windSpeed": 3.55, 826 | "windGust": 3.82, 827 | "windBearing": 274, 828 | "cloudCover": 0.22, 829 | "uvIndex": 9, 830 | "visibility": 10, 831 | "ozone": 249.23 832 | }, 833 | { 834 | "time": 1551681000, 835 | "summary": "Humid and Partly Cloudy", 836 | "icon": "partly-cloudy-day", 837 | "precipIntensity": 0, 838 | "precipProbability": 0, 839 | "temperature": 88.51, 840 | "apparentTemperature": 97.45, 841 | "dewPoint": 73.98, 842 | "humidity": 0.62, 843 | "pressure": 1010.51, 844 | "windSpeed": 5.19, 845 | "windGust": 5.31, 846 | "windBearing": 273, 847 | "cloudCover": 0.28, 848 | "uvIndex": 11, 849 | "visibility": 10, 850 | "ozone": 248.66 851 | }, 852 | { 853 | "time": 1551684600, 854 | "summary": "Humid and Partly Cloudy", 855 | "icon": "partly-cloudy-day", 856 | "precipIntensity": 0, 857 | "precipProbability": 0, 858 | "temperature": 88.57, 859 | "apparentTemperature": 97.8, 860 | "dewPoint": 74.23, 861 | "humidity": 0.63, 862 | "pressure": 1009.23, 863 | "windSpeed": 7.62, 864 | "windGust": 7.77, 865 | "windBearing": 276, 866 | "cloudCover": 0.35, 867 | "uvIndex": 10, 868 | "visibility": 10, 869 | "ozone": 247.82 870 | }, 871 | { 872 | "time": 1551688200, 873 | "summary": "Humid and Partly Cloudy", 874 | "icon": "partly-cloudy-day", 875 | "precipIntensity": 0.0002, 876 | "precipProbability": 0.02, 877 | "precipType": "rain", 878 | "temperature": 88.31, 879 | "apparentTemperature": 97.7, 880 | "dewPoint": 74.47, 881 | "humidity": 0.64, 882 | "pressure": 1007.96, 883 | "windSpeed": 10.21, 884 | "windGust": 11.02, 885 | "windBearing": 273, 886 | "cloudCover": 0.32, 887 | "uvIndex": 9, 888 | "visibility": 10, 889 | "ozone": 246.96 890 | }, 891 | { 892 | "time": 1551691800, 893 | "summary": "Humid and Partly Cloudy", 894 | "icon": "partly-cloudy-day", 895 | "precipIntensity": 0.0003, 896 | "precipProbability": 0.01, 897 | "precipType": "rain", 898 | "temperature": 88, 899 | "apparentTemperature": 97.4, 900 | "dewPoint": 74.59, 901 | "humidity": 0.65, 902 | "pressure": 1007.19, 903 | "windSpeed": 10.48, 904 | "windGust": 13.86, 905 | "windBearing": 246, 906 | "cloudCover": 0.29, 907 | "uvIndex": 7, 908 | "visibility": 10, 909 | "ozone": 246.34 910 | }, 911 | { 912 | "time": 1551695400, 913 | "summary": "Humid and Partly Cloudy", 914 | "icon": "partly-cloudy-day", 915 | "precipIntensity": 0.0002, 916 | "precipProbability": 0.01, 917 | "precipType": "rain", 918 | "temperature": 87.42, 919 | "apparentTemperature": 96.62, 920 | "dewPoint": 74.59, 921 | "humidity": 0.66, 922 | "pressure": 1006.92, 923 | "windSpeed": 6.62, 924 | "windGust": 15.99, 925 | "windBearing": 278, 926 | "cloudCover": 0.29, 927 | "uvIndex": 4, 928 | "visibility": 10, 929 | "ozone": 246 930 | }, 931 | { 932 | "time": 1551699000, 933 | "summary": "Humid and Partly Cloudy", 934 | "icon": "partly-cloudy-day", 935 | "precipIntensity": 0, 936 | "precipProbability": 0, 937 | "temperature": 86.5, 938 | "apparentTemperature": 95.31, 939 | "dewPoint": 74.58, 940 | "humidity": 0.68, 941 | "pressure": 1007, 942 | "windSpeed": 10.69, 943 | "windGust": 17.29, 944 | "windBearing": 310, 945 | "cloudCover": 0.28, 946 | "uvIndex": 1, 947 | "visibility": 10, 948 | "ozone": 245.84 949 | }, 950 | { 951 | "time": 1551702600, 952 | "summary": "Humid and Partly Cloudy", 953 | "icon": "partly-cloudy-day", 954 | "precipIntensity": 0.0011, 955 | "precipProbability": 0.03, 956 | "precipType": "rain", 957 | "temperature": 85.44, 958 | "apparentTemperature": 93.87, 959 | "dewPoint": 74.67, 960 | "humidity": 0.7, 961 | "pressure": 1007.49, 962 | "windSpeed": 11.1, 963 | "windGust": 17.18, 964 | "windBearing": 282, 965 | "cloudCover": 0.28, 966 | "uvIndex": 0, 967 | "visibility": 10, 968 | "ozone": 245.82 969 | } 970 | ] 971 | --------------------------------------------------------------------------------