diff --git a/MainActivity.java b/MainActivity.java deleted file mode 100644 index 9d29a72..0000000 --- a/MainActivity.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.ionic.starter; - -import com.getcapacitor.BridgeActivity; - -import com.codetrixstudio.capacitor.GoogleAuth.GoogleAuth; -import android.os.Bundle; // required for onCreate parameter - -public class MainActivity extends BridgeActivity { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - registerPlugin(GoogleAuth.class); - } -} diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js deleted file mode 100644 index 22bd9d9..0000000 --- a/e2e/protractor.conf.js +++ /dev/null @@ -1,37 +0,0 @@ -// @ts-check -// Protractor configuration file, see link for more information -// https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); - -/** - * @type { import("protractor").Config } - */ -exports.config = { - allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], - capabilities: { - browserName: 'chrome' - }, - directConnect: true, - SELENIUM_PROMISE_MANAGER: false, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} - }, - onPrepare() { - require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.json') - }); - jasmine.getEnv().addReporter(new SpecReporter({ - spec: { - displayStacktrace: StacktraceOption.PRETTY - } - })); - } -}; diff --git a/e2e/src/app.e2e-spec.ts b/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 33efa08..0000000 --- a/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { AppPage } from './app.po'; - -describe('new App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should be blank', () => { - page.navigateTo(); - expect(page.getParagraphText()).toContain('Start with Ionic UI Components'); - }); -}); diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts deleted file mode 100644 index c121fd9..0000000 --- a/e2e/src/app.po.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { browser, by, element } from 'protractor'; - -export class AppPage { - navigateTo() { - return browser.get('/'); - } - - getParagraphText() { - return element(by.deepCss('app-root ion-content')).getText(); - } -} diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json deleted file mode 100644 index a82df00..0000000 --- a/e2e/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/e2e", - "module": "commonjs", - "target": "es2018", - "types": [ - "jasmine", - "node" - ] - } -} diff --git a/src/app/app.component.html b/src/app/app.component.html index df6ff42..b5de982 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,13 +1,13 @@ - + - - M O B I S + M O B I S + - + @@ -15,25 +15,25 @@ - + {{ "menu.account" | translate }} - + {{ "menu.signup" | translate }} - + {{ "menu.signin" | translate }} - + {{ "menu.profile" | translate }} @@ -48,7 +48,7 @@ {{ "menu.settings" | translate }} - + {{ "menu.options" | translate }} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index aa6715f..855eda4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,12 +4,10 @@ import { RouteReuseStrategy } from '@angular/router'; import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; -import { initializeApp, provideFirebaseApp } from '@angular/fire/app'; -import { environment } from '../environments/environment'; -import { provideAuth, getAuth } from '@angular/fire/auth'; +import { getApp, initializeApp, provideFirebaseApp } from '@angular/fire/app'; +import { environment } from './app.constant'; import { provideFirestore, getFirestore } from '@angular/fire/firestore'; import { provideStorage, getStorage } from '@angular/fire/storage'; - import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { HttpClient, HttpClientModule } from '@angular/common/http'; @@ -20,6 +18,14 @@ import { IonicStorageModule } from '@ionic/storage-angular'; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, './assets/languages/', '.json'); } +//imports for IOS fix +import { + getAuth, + indexedDBLocalPersistence, + initializeAuth, + provideAuth, +} from '@angular/fire/auth'; +import { Capacitor } from '@capacitor/core'; @NgModule({ declarations: [AppComponent], @@ -31,7 +37,15 @@ export function createTranslateLoader(http: HttpClient) { IonicStorageModule.forRoot(), TranslateModule.forRoot({loader: {provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [HttpClient]}}), provideFirebaseApp(() => initializeApp(environment.firebase)), - provideAuth(() => getAuth()), + provideAuth(() => { + if (Capacitor.isNativePlatform()) { + return initializeAuth(getApp(), { + persistence: indexedDBLocalPersistence, + }); + } else { + return getAuth(); + } + }), provideFirestore(() => getFirestore()), provideStorage(() => getStorage()), ], diff --git a/src/app/auth/login/login.page.html b/src/app/auth/login/login.page.html index 969a31a..92b5256 100644 --- a/src/app/auth/login/login.page.html +++ b/src/app/auth/login/login.page.html @@ -1,7 +1,8 @@ - + - + + {{ "loginPage.title" | translate}} diff --git a/src/app/auth/login/login.page.ts b/src/app/auth/login/login.page.ts index 47cfe11..0220a33 100644 --- a/src/app/auth/login/login.page.ts +++ b/src/app/auth/login/login.page.ts @@ -82,7 +82,7 @@ export class LoginPage implements OnInit { await loading.present(); this.authService .loginWithGoogle() - .then(() => this.router.navigate(['/profile'])) + .then(() => this.router.navigate(['/home'])) .catch((e) => console.log(e.message)); await loading.dismiss(); } @@ -96,7 +96,7 @@ export class LoginPage implements OnInit { await loading.present(); this.authService .loginWithApple() - .then(() => this.router.navigate(['/profile'])) + .then(() => this.router.navigate(['/home'])) .catch((e) => console.log(e.message)); await loading.dismiss(); } @@ -110,7 +110,7 @@ export class LoginPage implements OnInit { await loading.present(); this.authService .anonymousLogin() - .then(() => this.router.navigate(['/profile'])) + .then(() => this.router.navigate(['/home'])) .catch((e) => console.log(e.message)); await loading.dismiss(); } diff --git a/src/app/auth/profile/profile.page.html b/src/app/auth/profile/profile.page.html index aacecb6..00139bb 100644 --- a/src/app/auth/profile/profile.page.html +++ b/src/app/auth/profile/profile.page.html @@ -1,7 +1,7 @@ - + - + {{ "profilePage.title" | translate }} @@ -17,6 +17,7 @@ +
+
+ + Delete Avatar + Get Weather + +
@@ -100,11 +107,14 @@ >{{ "profilePage.anonymous login text" | translate }} + {{ "profilePage.logout-button" | translate }} + {{ "profilePage.delete-profile-button" | translate }} +
{{ "profilePage.logged out" | translate }} - Please login to view your profile + Please login to view your profile diff --git a/src/app/auth/profile/profile.page.scss b/src/app/auth/profile/profile.page.scss index 83feb6f..19f6120 100644 --- a/src/app/auth/profile/profile.page.scss +++ b/src/app/auth/profile/profile.page.scss @@ -34,7 +34,7 @@ ion-avatar { .preview { //margin-top: 50px; display: flex; - justify-content: center; + justify-content: left; } .fallback { diff --git a/src/app/auth/profile/profile.page.ts b/src/app/auth/profile/profile.page.ts index 75170e9..66d9d35 100644 --- a/src/app/auth/profile/profile.page.ts +++ b/src/app/auth/profile/profile.page.ts @@ -6,6 +6,7 @@ import { user, Auth } from '@angular/fire/auth'; import { AvatarService } from '../../services/avatar.service'; import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; import { TranslateService } from '@ngx-translate/core'; +import { WeatherService } from 'src/app/services/weather.service'; @Component({ selector: 'app-profile', @@ -30,24 +31,15 @@ export class ProfilePage implements OnInit { private loadingController: LoadingController, private alertController: AlertController, private afAuth: Auth, - private translateService: TranslateService + private translateService: TranslateService, + private weatherService: WeatherService ) { - // not the nicest way, sort of duplicate to have a URL in the profile variable and also check later the user variable - this.avatarService.getUserProfile().subscribe((data) => { - if (data) { - this.profile = data; - if (this.profile.imageUrl !== null) { - this.photoURL = this.profile.imageUrl; - } - } - //console.log('data' , data); - }); user(this.afAuth).subscribe((response) => { //fill the user to verify if someone is logged in this.user = response; if (response !== null) { - console.log(response); + //console.log(response); this.displayName = response.displayName; this.email = response.email; this.photoURL = response.photoURL; @@ -64,6 +56,12 @@ export class ProfilePage implements OnInit { } ngOnInit(): void {} + deleteAvatar(){ + this.avatarService.removeImage(); //remove image from storage + this.photoURL = null; //remove image from profile page + this.authService.updatePhotoURL(''); //remove profile url from profile + } + editDisplayNameField() { this.editDisplayName = true; } @@ -94,6 +92,16 @@ export class ProfilePage implements OnInit { await this.authService.logout(); this.router.navigateByUrl('/home', { replaceUrl: true }); } + //delete user in firebase project, not actual google profile + deleteAccount(){ + //todo: show pop up with question if user really wants to delete his account if clicked yes, delete account + this.authService.deleteAccount(); + this.router.navigateByUrl('/home', { replaceUrl: true }); + } + + getWeather() { + this.weatherService.showCurrentWeather(); + } async changeImage() { const image = await Camera.getPhoto({ diff --git a/src/app/auth/resetpw/resetpw.page.html b/src/app/auth/resetpw/resetpw.page.html index a80dacf..75a49bb 100644 --- a/src/app/auth/resetpw/resetpw.page.html +++ b/src/app/auth/resetpw/resetpw.page.html @@ -1,8 +1,8 @@ - + {{ "resetpwPage.title" | translate }} - + diff --git a/src/app/auth/signup/signup.page.html b/src/app/auth/signup/signup.page.html index e3889bd..4b2fbac 100644 --- a/src/app/auth/signup/signup.page.html +++ b/src/app/auth/signup/signup.page.html @@ -1,7 +1,7 @@ - + - + {{ "signupPage.title" | translate }} diff --git a/src/app/environments/environment.prod.ts b/src/app/environments/environment.prod.ts new file mode 100644 index 0000000..5ca5057 --- /dev/null +++ b/src/app/environments/environment.prod.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +export const environment = { + production: true, + DDQparseServerURL: 'https://parse.ddq.nl/parse', + DDQDSMServerURL: 'https://prod3.ddq.nl/dsm', + DDQMobisServerURL: 'https://mobis.ddq.nl/parse', + DDQparseDSMJSKey: 'hEH22V1339sNBNu8HGdHzKsU5iAHSrTqKA3ZIsCde', + DDQparseDSMAppId: 'iiNtsbeC8qZfzX243hZFUiV03bcGBkiBXtRMIZGG', + DDQparseMOBISJSKey: '15Inx8rIAqZGhlEDNixoNHb8X8KioVg4DuMX7Bk7E', + DDQparseMOBISAppId: '5yHaDCnG17ySDKKQ0BqL13eWOMoygILWmwnYjDbuDOE', + Back4AppAPIURL: 'https://parseapi.back4app.com', + Back4AppSpecificURL: 'https://parseapi.back4app.com/classes/Test', + Back4AppJSKey: '0BRb0a2x57VjB4FV11nJZ37vTDuHHda74u2JS6yJ', + Back4AppAppId: '3z7ETB9kJIjTNdligGBnGPfWRh4dMtUCbxpvDNSd', + + firebase: { + projectId: 'mobisapp-prod', + appId: '1:1090658128897:web:2d91733ed9e7b992a2dc00', + storageBucket: 'mobisapp-prod.appspot.com', + //locationId: 'europe-west', + apiKey: 'AIzaSyDmgEw1MaPmTWyiFhlrywo5um8x_3Py-no', + authDomain: 'mobisapp-prod.firebaseapp.com', + messagingSenderId: '1090658128897', + } + +}; diff --git a/src/app/environments/environment.ts b/src/app/environments/environment.ts new file mode 100644 index 0000000..b081164 --- /dev/null +++ b/src/app/environments/environment.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + DDQparseServerURL: 'https://parse.ddq.nl/parse', + DDQparseDSMJSKey: 'hEH22V1339sNBNu8HGdHzKsU5iAHSrTqKA3ZIsCde', + DDQparseDSMAppId: 'iiNtsbeC8qZfzX243hZFUiV03bcGBkiBXtRMIZGG', + DDQparseMOBISKey: '15Inx8rIAqZGhlEDNixoNHb8X8KioVg4DuMX7Bk7E', + DDQparseMOBISAppId: '5yHaDCnG17ySDKKQ0BqL13eWOMoygILWmwnYjDbuDOE', + Back4AppAPIURL: 'https://parseapi.back4app.com', + Back4AppSpecificURL: 'https://parseapi.back4app.com/classes/Test', + Back4AppJSKey: '0BRb0a2x57VjB4FV11nJZ37vTDuHHda74u2JS6yJ', + Back4AppAppId: '3z7ETB9kJIjTNdligGBnGPfWRh4dMtUCbxpvDNSd', + + //COPIED PROD values to test issue with capacitor + firebase: { + projectId: 'mobisapp-prod', + appId: '1:1090658128897:web:2d91733ed9e7b992a2dc00', + storageBucket: 'mobisapp-prod.appspot.com', + //locationId: 'europe-west', + apiKey: 'AIzaSyDmgEw1MaPmTWyiFhlrywo5um8x_3Py-no', + authDomain: 'mobisapp-prod.firebaseapp.com', + messagingSenderId: '1090658128897', + } + + //DEV VALUES + // firebase: { + // projectId: 'mobisapp-dev', + // appId: '1:898286986083:web:ed0ad74a2e48623edf2956', + // storageBucket: 'mobisapp-dev.appspot.com', + // //locationId: 'europe-west', + // apiKey: 'AIzaSyBGVeTDnHDx_WSHZXiynNI8QnvNQXfkmxc', + // authDomain: 'mobisapp-dev.firebaseapp.com', + // messagingSenderId: '898286986083', + // }, +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/src/app/home/home-routing.module.ts b/src/app/home/home-routing.module.ts index 29c3f60..6aa177b 100644 --- a/src/app/home/home-routing.module.ts +++ b/src/app/home/home-routing.module.ts @@ -1,12 +1,26 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomePage } from './home.page'; +import {redirectUnauthorizedTo, redirectLoggedInTo, canActivate } from '@angular/fire/auth-guard'; + +const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']); +const redirectLoggedInToProfile = () => redirectLoggedInTo(['profile']); const routes: Routes = [ { path: '', component: HomePage, - } + }, + { path: 'login', +loadChildren: () => import('../auth/login/login.module').then( m => m.LoginPageModule) +}, + +{ + path: 'profile', + loadChildren: () => import('../auth/profile/profile.module').then( m => m.ProfilePageModule), + ...canActivate(redirectUnauthorizedToLogin) +}, + ]; @NgModule({ diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts index 9ed097f..af0108f 100644 --- a/src/app/home/home.module.ts +++ b/src/app/home/home.module.ts @@ -7,7 +7,6 @@ import { HomePage } from './home.page'; import { HomePageRoutingModule } from './home-routing.module'; import { TranslateModule } from '@ngx-translate/core'; - @NgModule({ imports: [ CommonModule, diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index 95b8e37..be337c0 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -1,56 +1,84 @@ - - - M O B I S + + + M O B I S - - - - - + + + + + + + + {{ "measure.welcome-to-mobis" | translate }} + + + {{ "menu.account" | translate }} + + + {{ "menu.login-register" | translate }} + - + + + {{ "menu.profile" | translate }} + - - {{ "measure.welcome-to-mobis" | translate }} - {{ "measure.measure" | translate }} - - - - {{ "measure.water-quality" | translate }} - - - {{ "measure.new-mini-secchi-observation" | translate }} + + + {{ "menu.signout" | translate }} + + + + - - {{ "measure.new-ispex2-observation" | translate }} + + {{ "measure.measure" | translate }} + + - - {{ "measure.air-quality" | translate }} - - - {{ "measure.new-canairiopm25-observation" | translate }} - + + {{ "measure.environment" | translate }} + + + + + {{ "measure.envpic" | translate }} + + + + {{ "measure.ispex2-observation" | translate }} + + + + {{ "measure.water-quality" | translate }} + + + {{ "measure.mini-secchi-observation" | translate }} + - - {{ "measure.new-canairioco2-observation" | translate }} - - - {{ "measure.biodiversity" | translate }} - - - {{ "measure.new-plantnet-observation" | translate }} - - - - - - - + + {{ "measure.air-quality" | translate }} + + + {{ "measure.canairiopm2.5-observation" | translate }} + + + {{ "measure.canairioco2-observation" | translate }} + + + {{ "measure.biodiversity" | translate }} + + + {{ "measure.plantnet-observation" | translate }} + + + + diff --git a/src/app/home/home.page.scss b/src/app/home/home.page.scss index d2d19ec..cb2c61d 100644 --- a/src/app/home/home.page.scss +++ b/src/app/home/home.page.scss @@ -1,128 +1,7 @@ -#container { - text-align: center; - - position: absolute; - left: 0; - right: 0; - top: 50%; - transform: translateY(-50%); -} - -#container strong { - font-size: 20px; - line-height: 26px; -} - -#container p { - font-size: 16px; - line-height: 22px; - - color: #8c8c8c; - - margin: 0; -} - -#container a { - text-decoration: none; -} - -ion-avatar { - width: 128px; - height: 128px; -} - -.preview { - margin-top: 50px; - display: flex; - justify-content: center; -} - -.fallback { - width: 128px; - height: 128px; - border-radius: 50%; - background: #bfbfbf; - - display: flex; - justify-content: center; - align-items: center; - font-weight: 500; -} - -ion-label.title{ +ion-title { + font-size: 1.5em; font-weight: bold; + color: white; + text-transform: uppercase; + font-family: Open Sans, Helvetica, sans-serif; } - -ion-col{ - text-align: start; - vertical-align: middle; -} - -ion-col ion-button{ - vertical-align: baseline; -} - -ion-icon { - pointer-events: none; -} - -ion-card{ - padding: 0px; - margin: 2px; - - ion-card-header{ - font-weight: bold; - } - - ion-card-content{ - font-size: 12px; - } -} - -ion-header ion-img{ - width:50px; -} -ion-img{ - // display: block; - margin-left: auto; - margin-right: auto; - width: 80%; -} - -ion-col { - display: flex; - justify-content: center; - padding: 0px; -} - -ion-footer{ - padding-top: 2px; - color: black; - display: flex; - align-items:center; - justify-content: center; - - border-top: 5px, solid; - border-color: #8c8c8c; - background-color: rgb(255, 255, 255); - -} - -.icon-verified { - display: inline-block; - font-size: 20px; - color: #1a7900; - vertical-align: middle; - } - - .icon-not-verified { - display: inline-block; - font-size: 20px; - color: #790000; - vertical-align: middle; - } - - .text{ - display: inline-block; - vertical-align: middle; - } diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index 63749bd..62f4f27 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -2,7 +2,10 @@ import { Component, OnInit } from '@angular/core'; import { MenuController } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; import { Geolocation, PositionOptions } from '@capacitor/geolocation'; - +import { AuthService } from '../services/auth.service'; +import { Auth, user } from '@angular/fire/auth'; +import { Storage } from '@ionic/storage-angular'; +import { Router } from '@angular/router'; @Component({ selector: 'app-home', @@ -11,11 +14,23 @@ import { Geolocation, PositionOptions } from '@capacitor/geolocation'; }) export class HomePage implements OnInit{ + user = null; + language= ''; constructor( - private menuController: MenuController, private translateService: TranslateService + private menuController: MenuController, + private translateService: TranslateService, + private authService: AuthService, + private afAuth: Auth, + private storage: Storage, + private router: Router + ) { + user(this.afAuth).subscribe((response) => { + //fill the user to verify if someone is logged in + this.user = response; + }); @@ -35,7 +50,23 @@ export class HomePage implements OnInit{ console.log('Current location: ', location); + this.storage.create(); + this.translateService.setDefaultLang('en'); //set default language English + this.language = await this.storage.get('language'); + if (this.language === null){ + console.log('no value for language in storage, trying getting from browser'); + this.language = this.translateService.getBrowserLang(); + + } + this.translateService.use(this.language); + + + } + + async logout() { + await this.authService.logout(); + this.router.navigateByUrl('/home', { replaceUrl: true }); } openMenu(){ diff --git a/src/app/mobisplugins/canairioco2/canairioco2.page.html b/src/app/mobisplugins/canairioco2/canairioco2.page.html index a72de80..bb1743b 100644 --- a/src/app/mobisplugins/canairioco2/canairioco2.page.html +++ b/src/app/mobisplugins/canairioco2/canairioco2.page.html @@ -1,82 +1,56 @@ - - - New Canair.io CO2 observation - + + - - - - - - - - - - - - - - - - Canairio output - - + - - - CO2: {{CO2}} ({{CO2Interpret}}) - - - - - + + CANAIR.IO CO2 + + + + + + Canairio output + + + + CO2: {{CO2}} ({{CO2Interpret}}) + + + Temperature: {{TEMP}} C - - - + + Humidity: {{HUMID }} % - - - - + + + PAX: {{PAX}} - - - - - - + + + Lat: {{latitude}} - - - - - + + + Lon: {{longitude}} - - - - + + + Alt: {{altitude}} - - - - - - - - Start logging - Stop logging - - - - Log Interval:{{logInterval}} - - - - Log status:{{lblLogstatus}} - - - - - \ No newline at end of file + + + + Start logging + Stop logging + + + + Log Interval: + {{logInterval}} + + + + Log status:{{lblLogstatus}} + + diff --git a/src/app/mobisplugins/canairioco2/canairioco2.page.ts b/src/app/mobisplugins/canairioco2/canairioco2.page.ts index 46b04e7..696e00e 100644 --- a/src/app/mobisplugins/canairioco2/canairioco2.page.ts +++ b/src/app/mobisplugins/canairioco2/canairioco2.page.ts @@ -1,23 +1,30 @@ +/* eslint-disable @typescript-eslint/member-ordering */ +/* eslint-disable @typescript-eslint/naming-convention */ import { Component, OnInit } from '@angular/core'; import { ENV } from '../../app.constant'; -import { Geolocation} from '@capacitor/geolocation'; -import { Guid } from "guid-typescript"; -import {interval, Subscription} from 'rxjs'; +import { Geolocation } from '@capacitor/geolocation'; +import { Guid } from 'guid-typescript'; +import { interval, Subscription } from 'rxjs'; import { Storage } from '@ionic/storage-angular'; import * as Parse from 'parse'; import { BackgroundMode } from '@awesome-cordova-plugins/background-mode/ngx'; - +import { TranslateService } from '@ngx-translate/core'; +import { AuthService } from '../../services/auth.service'; +import { Auth, user } from '@angular/fire/auth'; // Setup Bluetooth LE // Import the wrapper class directly -import { BleClient, numbersToDataView, numberToUUID, dataViewToText } from '@capacitor-community/bluetooth-le'; - +import { + BleClient, + numbersToDataView, + numberToUUID, + dataViewToText, +} from '@capacitor-community/bluetooth-le'; const PM25_SERVICE = 'C8D1D262-861F-4082-947E-F383A259AAF3'; -const PM25_SERVICE_LCASE= 'c8d1d262-861f-4082-947e-f383a259aaf3'; - +const PM25_SERVICE_LCASE = 'c8d1d262-861f-4082-947e-f383a259aaf3'; const PM25_SERVICE_CHARACTERISTIC = 'B0F332A8-A5AA-4F3F-BB43-F99E7791AE01'; /* other services "B0F332A8-A5AA-4F3F-BB43-F99E7791AE02", @@ -30,216 +37,219 @@ const PM25_SERVICE_CHARACTERISTIC = 'B0F332A8-A5AA-4F3F-BB43-F99E7791AE01'; styleUrls: ['./canairioco2.page.scss'], }) export class Canairioco2Page implements OnInit { -public txtco2: string; -public datetime_ux: string; -public datetime: Date; -public latitude: number; -public longitude: number; -public altitude: number; -private parseAppId: string = ENV.parseAppId; -private parseServerUrl: string = ENV.parseServerUrl; -private parseJSKey: string=ENV.parseJSKey; -public result: string; -public output_json: string; -public rec_uid: string; -public session_uid: string; + user = null; + language = ''; -public deviceId:string; -public intervalID:NodeJS.Timeout; -public logInterval:number; -public lblLogstatus: string; -public allData: any; -public CO2: string; -public TEMP: string; -public HUMID: string; -public PAX: string; -public CO2Interpret:string; + public txtco2: string; + public datetime_ux: string; + public datetime: Date; + public latitude: number; + public longitude: number; + public altitude: number; + private parseAppId: string = ENV.parseAppId; + private parseServerUrl: string = ENV.parseServerUrl; + private parseJSKey: string = ENV.parseJSKey; + public result: string; + public output_json: string; + public rec_uid: string; + public session_uid: string; -public ParseServerKey:string; -public ParseServerURL:string; -public ParseServerAppID:string; + public deviceId: string; + public intervalID: NodeJS.Timeout; + public logInterval: number; + public lblLogstatus: string; + public allData: any; + public CO2: string; + public TEMP: string; + public HUMID: string; + public PAX: string; + public CO2Interpret: string; + public ParseServerKey: string; + public ParseServerURL: string; + public ParseServerAppID: string; -constructor(private backgroundMode: BackgroundMode) { } - - -// Initialize Parse SDK and connect to Parse Server - - -ngOnInit() { - -this.parseInitialize(); -this.getLocation(); -this.connect(); -this.logInterval=5000; -this.lblLogstatus="Not logging"; - this.readandSave(); - - this.parseAppId=ENV.parseAppId; - this.parseServerUrl=ENV.parseServerUrl; - this.parseJSKey=ENV.parseJSKey; - + isLogging = false; + constructor( + private backgroundMode: BackgroundMode, + private translateService: TranslateService, + private authService: AuthService, + private afAuth: Auth, + private storage: Storage + ) { + user(this.afAuth).subscribe((response) => { + //fill the user to verify if someone is logged in + this.user = response; + console.log(this.user.uid); + }); } -// i am not sure if this is needed, because CO2 is not moving + // Initialize Parse SDK and connect to Parse Server -async startLogging() { + ngOnInit() { + this.parseInitialize(); + this.getLocation(); + this.connect(); + this.logInterval = 5000; + this.lblLogstatus = 'Not logging'; - this.backgroundMode.enable(); + this.parseAppId = ENV.parseAppId; + this.parseServerUrl = ENV.parseServerUrl; + this.parseJSKey = ENV.parseJSKey; -this.lblLogstatus="Logging.."; -// set session uid -this.session_uid = Guid.raw(); // make it a string -var str_counter; - str_counter=0; + this.readandSave(); + } + // i am not sure if this is needed, because CO2 is not moving - this.intervalID = setInterval( () => { + async startLogging() { + this.backgroundMode.enable(); + this.lblLogstatus = 'Logging..'; + // set session uid + this.session_uid = Guid.raw(); // make it a string + var str_counter; + str_counter = 0; -this.lblLogstatus="Logging: " + str_counter.toString() ; + this.intervalID = setInterval(() => { + this.lblLogstatus = 'Logging: ' + str_counter.toString(); + this.isLogging = true; + str_counter++; - str_counter++; + this.getLocation(); + this.readandSave(); + }, this.logInterval); + } - this.getLocation(); - this.readandSave(); + async stopLogging() { + clearInterval(this.intervalID); + this.lblLogstatus = 'Not logging'; + this.isLogging = false; - },this.logInterval); + this.backgroundMode.disable(); + } + async readandSave() { + const result = await BleClient.read( + this.deviceId, + PM25_SERVICE, + PM25_SERVICE_CHARACTERISTIC + ); + console.log('canair.io result array', dataViewToText(result)); + this.allData = JSON.parse(dataViewToText(result)); // parse json data and pass json string -} + this.CO2 = this.allData['CO2']; + // Indoor CO2 levels + if (Number(this.CO2) > 0 && Number(this.CO2) < 700) + this.CO2Interpret = 'Excellent'; + if (Number(this.CO2) >= 700 && Number(this.CO2) < 800) + this.CO2Interpret = 'Good'; + if (Number(this.CO2) >= 800 && Number(this.CO2) < 1000) + this.CO2Interpret = 'Fair'; + if (Number(this.CO2) >= 1000 && Number(this.CO2) < 1500) + this.CO2Interpret = 'Mediocre'; + if (Number(this.CO2) >= 1500) this.CO2Interpret = 'Bad'; -async stopLogging() { - clearInterval(this.intervalID); - this.lblLogstatus="Not logging"; - - this.backgroundMode.disable(); - - -} - - -async readandSave() { - - const result = await BleClient.read(this.deviceId, PM25_SERVICE, PM25_SERVICE_CHARACTERISTIC); - console.log('canair.io result array', dataViewToText(result)); - -this.allData = JSON.parse(dataViewToText(result)); // parse json data and pass json string - - - - -this.CO2= this.allData['CO2']; - - -// Indoor CO2 levels -if (Number(this.CO2)>0 && Number(this.CO2)<700 ) this.CO2Interpret= ("Excellent"); -if (Number(this.CO2)>=700 && Number(this.CO2)<800 )this.CO2Interpret= ("Good"); -if (Number(this.CO2)>=800 && Number(this.CO2)<1000 )this.CO2Interpret= ("Fair"); -if (Number(this.CO2)>=1000 && Number(this.CO2)<1500 )this.CO2Interpret= ("Mediocre"); -if (Number(this.CO2)>=1500 ) this.CO2Interpret= ("Bad"); - - - -this.TEMP= this.allData['tmp']; -this.HUMID= this.allData['hum']; -this.PAX= this.allData['PAX']; -this.CO2= this.allData['CO2']; - -// lets assume we have -if (Number(this.CO2)>0) { -this.TEMP= this.allData['CO2T']; -this.HUMID= this.allData['CO2H']; - -} - - - - -// this.txtco2= this.allData['CO2']; // this is the CO2 value - -let d = new Date(); -this.datetime=d; -var unixTimeStamp = Math.floor(d.getTime() / 1000); -this.datetime_ux=unixTimeStamp.toString(); -this.output_json=dataViewToText(result); -this.rec_uid = Guid.raw(); // make it a string - -var Comment = Parse.Object.extend('canairico2_raw_data'); -var canairio_store = new Comment(); -// set initial data record -canairio_store.set('session_UID',this.session_uid); -canairio_store.set('record_UID',this.rec_uid); -canairio_store.set('device_UID',this.deviceId); -canairio_store.set('output_json',dataViewToText(result)); -canairio_store.set('TEMP',this.TEMP); -canairio_store.set('HUMID',this.HUMID); -canairio_store.set('CO2',this.CO2); -canairio_store.set('PAX',this.PAX); -canairio_store.set('latitude',this.latitude); -canairio_store.set('longitude',this.longitude); -canairio_store.set('altitude',this.altitude); -canairio_store.set('datetime_ux',this.datetime_ux); -await canairio_store.save(); - -} - - - - -// get location and save to class variables - - async getLocation() { - const position = await Geolocation.getCurrentPosition({enableHighAccuracy: true}); - this.latitude = position.coords.latitude; - console.log (position.coords.latitude); - this.longitude = position.coords.longitude; - this.altitude = position.coords.altitude; - return position.coords; -} - -// connect to parse server and initialize - - private parseInitialize() { - - Parse.initialize(this.parseAppId, this.parseJSKey); - - (Parse as any).serverURL = this.ParseServerURL; // use your server url + this.TEMP = this.allData['tmp']; + this.HUMID = this.allData['hum']; + this.PAX = this.allData['PAX']; + this.CO2 = this.allData['CO2']; + // lets assume we have + if (Number(this.CO2) > 0) { + this.TEMP = this.allData['CO2T']; + this.HUMID = this.allData['CO2H']; } + // this.txtco2= this.allData['CO2']; // this is the CO2 value - async connect(){ + const d = new Date(); + this.datetime = d; + const unixTimeStamp = Math.floor(d.getTime() / 1000); + this.datetime_ux = unixTimeStamp.toString(); + this.output_json = dataViewToText(result); + this.rec_uid = Guid.raw(); // make it a string\ - try { - await BleClient.initialize(); - const device = await BleClient.requestDevice({ - namePrefix: 'CanAirIO', - optionalServices : [PM25_SERVICE_LCASE] - }); + // The parse part + // create parse class + const canairio_data_store = Parse.Object.extend('canairio_raw_data'); - // console.log('device', device); + // create new instance of parse class + const canairio_store = new canairio_data_store(); - await BleClient.connect(device.deviceId); - console.log('connected to device', device); + // set value for parse clas - this.deviceId=device.deviceId; -this.readandSave(); + // set initial data record + canairio_store.set('session_UID', this.session_uid); + canairio_store.set('record_UID', this.rec_uid); + canairio_store.set('device_UID', this.deviceId); + canairio_store.set('output_json', dataViewToText(result)); + canairio_store.set('TEMP', this.TEMP); + canairio_store.set('HUMID', this.HUMID); + canairio_store.set('CO2', this.CO2); + canairio_store.set('PAX', this.PAX); + canairio_store.set('latitude', this.latitude); + canairio_store.set('longitude', this.longitude); + canairio_store.set('altitude', this.altitude); - - setTimeout(async () => { - await BleClient.stopLEScan(); - console.log('stopped scanning'); - }, 5000); - } catch (error) { - console.error(error); + canairio_store.set('datetime_ux', this.datetime_ux); + canairio_store.set('user_uid', this.user.uid); + canairio_store.set('name', this.user.displayName); + canairio_store.set('email', this.user.email); + //log the parse save to console + canairio_store.save().then( + (saveResult: any) => { + console.log(saveResult); + }, + (error: any) => { + console.log(error); + } + ); } + // get location and save to class variables + async getLocation() { + const position = await Geolocation.getCurrentPosition({ + enableHighAccuracy: true, + }); + this.latitude = position.coords.latitude; + console.log(position.coords.latitude); + this.longitude = position.coords.longitude; + this.altitude = position.coords.altitude; + return position.coords; + } -} - - + // connect to parse server and initialize + private parseInitialize() { + Parse.initialize(ENV.parseAppId, ENV.parseJSKey); + (Parse as any).serverURL = ENV.parseServerUrl; // use your server url + } + + async connect() { + try { + await BleClient.initialize(); + const device = await BleClient.requestDevice({ + namePrefix: 'CanAirIO', + optionalServices: [PM25_SERVICE_LCASE], + }); + + // console.log('device', device); + + await BleClient.connect(device.deviceId); + console.log('connected to device', device); + + this.deviceId = device.deviceId; + this.readandSave(); + + setTimeout(async () => { + await BleClient.stopLEScan(); + console.log('stopped scanning'); + }, 5000); + } catch (error) { + console.error(error); + } + } } diff --git a/src/app/mobisplugins/canairiopm25/canairiopm25.page.html b/src/app/mobisplugins/canairiopm25/canairiopm25.page.html index a041811..7cfa562 100644 --- a/src/app/mobisplugins/canairiopm25/canairiopm25.page.html +++ b/src/app/mobisplugins/canairiopm25/canairiopm25.page.html @@ -1,85 +1,59 @@ - - - New Canair.io PM2.5 observation - - - - + + + + + + CANAIR.IO PM2.5 - - - - - - - - -Canairio output - + + Canairio output + + + {{txtpm25}} - - {{txtpm25}} - + + PM 2.5: {{PM25}} ({{PM25Interpret}}) + - - PM 2.5: {{PM25}} ({{PM25Interpret}}) + + Temperature: {{TEMP}} C + + + Humidity: {{HUMID }} % + - + + PAX: {{PAX}} + + + Lat: {{latitude}} + + + Lon: {{longitude}} + - - Temperature: {{TEMP}} C + + Alt: {{altitude}} + - - - Humidity: {{HUMID }} % - - - - - PAX: {{PAX}} - - - - - - - Lat: {{latitude}} - - - - - - Lon: {{longitude}} - - - - - Alt: {{altitude}} - - - - - - - -Start logging - Stop logging - - - -Log Interval:{{logInterval}} - - - -Log status:{{lblLogstatus}} - + + Start logging + Stop logging + + + Log Interval: + {{logInterval}} + + + Log status:{{lblLogstatus}} + diff --git a/src/app/mobisplugins/canairiopm25/canairiopm25.page.ts b/src/app/mobisplugins/canairiopm25/canairiopm25.page.ts index c938433..099e739 100644 --- a/src/app/mobisplugins/canairiopm25/canairiopm25.page.ts +++ b/src/app/mobisplugins/canairiopm25/canairiopm25.page.ts @@ -1,23 +1,27 @@ - +/* eslint-disable @typescript-eslint/member-ordering */ +/* eslint-disable @typescript-eslint/naming-convention */ import { Component, OnInit } from '@angular/core'; import { ENV } from '../../app.constant'; -import { Geolocation} from '@capacitor/geolocation'; -import { Guid } from "guid-typescript"; -import {interval, Subscription} from 'rxjs'; +import { Geolocation } from '@capacitor/geolocation'; +import { Guid } from 'guid-typescript'; +import { interval, Subscription } from 'rxjs'; import { Storage } from '@ionic/storage-angular'; import * as Parse from 'parse'; import { BackgroundMode } from '@awesome-cordova-plugins/background-mode/ngx'; - - +import { AuthService } from '../../services/auth.service'; +import { Auth, user } from '@angular/fire/auth'; // Import the wrapper class directly -import { BleClient, numbersToDataView, numberToUUID, dataViewToText } from '@capacitor-community/bluetooth-le'; - +import { + BleClient, + numbersToDataView, + numberToUUID, + dataViewToText, +} from '@capacitor-community/bluetooth-le'; const PM25_SERVICE = 'C8D1D262-861F-4082-947E-F383A259AAF3'; -const PM25_SERVICE_LCASE= 'c8d1d262-861f-4082-947e-f383a259aaf3'; - +const PM25_SERVICE_LCASE = 'c8d1d262-861f-4082-947e-f383a259aaf3'; const PM25_SERVICE_CHARACTERISTIC = 'B0F332A8-A5AA-4F3F-BB43-F99E7791AE01'; /* other services "B0F332A8-A5AA-4F3F-BB43-F99E7791AE02", @@ -30,222 +34,217 @@ const PM25_SERVICE_CHARACTERISTIC = 'B0F332A8-A5AA-4F3F-BB43-F99E7791AE01'; styleUrls: ['./canairiopm25.page.scss'], }) export class Canairiopm25Page implements OnInit { -public txtpm25: string; -public datetime_ux: string; -public datetime: Date; -public latitude: number; -public longitude: number; -public altitude: number; -private parseAppId: string = ENV.parseAppId; -private parseServerUrl: string = ENV.parseServerUrl; -private parseJSKey: string=ENV.parseJSKey; -public result: string; -public output_json: string; -public rec_uid: string; -public session_uid: string; + image = ''; + user = null; + public txtpm25: string; + public datetime_ux: string; + public datetime: Date; + public latitude: number; + public longitude: number; + public altitude: number; + private parseAppId: string = ENV.parseAppId; + private parseServerUrl: string = ENV.parseServerUrl; + private parseJSKey: string = ENV.parseJSKey; + public result: string; + public output_json: string; + public rec_uid: string; + public session_uid: string; -public deviceId:string; -public intervalID:NodeJS.Timeout; -public logInterval:number; -public lblLogstatus: string; -public allData: any; -public PM25: string; -public CO2: string; -public TEMP: string; -public HUMID: string; -public PAX: string; -public PM25Interpret:string; - -public ParseServerKey:string; -public ParseServerURL:string; -public ParseServerAppID:string; - - -constructor(private backgroundMode: BackgroundMode) { } - - - - - -ngOnInit() { - -this.parseInitialize(); -this.getLocation(); -this.connect(); -this.logInterval=5000; -this.lblLogstatus="Not logging"; - this.readandSave(); - - this.parseAppId=ENV.parseAppId; - this.parseServerUrl=ENV.parseServerUrl; - this.parseJSKey=ENV.parseJSKey; + public deviceId: string; + public intervalID: NodeJS.Timeout; + public logInterval: number; + public lblLogstatus: string; + public allData: any; + public PM25: string; + public CO2: string; + public TEMP: string; + public HUMID: string; + public PAX: string; + public PM25Interpret: string; + public ParseServerKey: string; + public ParseServerURL: string; + public ParseServerAppID: string; + isLogging = false; + constructor( + private backgroundMode: BackgroundMode, + private authService: AuthService, + private afAuth: Auth + ) { + user(this.afAuth).subscribe((response) => { + //fill the user to verify if someone is logged in + this.user = response; + console.log(this.user.uid); + }); } + ngOnInit() { + this.parseInitialize(); + this.getLocation(); + this.connect(); + this.logInterval = 5000; + this.lblLogstatus = 'Not logging'; + this.readandSave(); + + this.parseAppId = ENV.parseAppId; + this.parseServerUrl = ENV.parseServerUrl; + this.parseJSKey = ENV.parseJSKey; + } + + async startLogging() { + this.backgroundMode.enable(); + + this.lblLogstatus = 'Logging..'; + this.isLogging = true; + // set session uid + this.session_uid = Guid.raw(); // make it a string + var str_counter; + str_counter = 0; + + this.intervalID = setInterval(() => { + this.lblLogstatus = 'Logging: ' + str_counter.toString(); + + str_counter++; + + this.getLocation(); + this.readandSave(); + }, this.logInterval); + } + + async stopLogging() { + clearInterval(this.intervalID); + this.lblLogstatus = 'Not logging'; + this.isLogging = false; + + this.backgroundMode.disable(); + } + + async readandSave() { + const result = await BleClient.read( + this.deviceId, + PM25_SERVICE, + PM25_SERVICE_CHARACTERISTIC + ); + console.log('canair.io result array', dataViewToText(result)); + + this.allData = JSON.parse(dataViewToText(result)); // parse json data and pass json string + + this.PM25 = this.allData['P25']; + + //outdoor + if (Number(this.PM25) > 0 && Number(this.PM25) < 25) + {this.PM25Interpret = 'Good';} + if (Number(this.PM25) > 24 && Number(this.PM25) < 51) + {this.PM25Interpret = 'Fair';} + if (Number(this.PM25) > 49 && Number(this.PM25) < 101) + {this.PM25Interpret = 'Poor';} + if (Number(this.PM25) > 100 && Number(this.PM25) < 300) + {this.PM25Interpret = 'Very Poor';} + if (Number(this.PM25) > 300) {this.PM25Interpret = 'Extremely Poor';} + + this.TEMP = this.allData['tmp']; + this.HUMID = this.allData['hum']; + this.PAX = this.allData['PAX']; + this.CO2 = this.allData['CO2']; + + // lets assume we have + if (Number(this.CO2) > 0) { + this.TEMP = this.allData['CO2T']; + this.HUMID = this.allData['CO2H']; + } + + const d = new Date(); + this.datetime = d; + const unixTimeStamp = Math.floor(d.getTime() / 1000); + this.datetime_ux = unixTimeStamp.toString(); + this.output_json = dataViewToText(result); + this.txtpm25 = this.output_json; + this.rec_uid = Guid.raw(); // make it a string + + // The parse part + // create parse class + + const canairio_data_store = Parse.Object.extend('canairio_raw_data'); -async startLogging() { + // create new instance of parse class - this.backgroundMode.enable(); - -this.lblLogstatus="Logging.."; -// set session uid -this.session_uid = Guid.raw(); // make it a string -var str_counter; - str_counter=0; + const canairio_store = new canairio_data_store(); - this.intervalID = setInterval( () => { + // set value for parse clas - -this.lblLogstatus="Logging: " + str_counter.toString() ; - - str_counter++; - - this.getLocation(); - this.readandSave(); - - },this.logInterval); - - - -} - - -async stopLogging() { - clearInterval(this.intervalID); - this.lblLogstatus="Not logging"; - - this.backgroundMode.disable(); - - -} - - -async readandSave() { - - const result = await BleClient.read(this.deviceId, PM25_SERVICE, PM25_SERVICE_CHARACTERISTIC); - console.log('canair.io result array', dataViewToText(result)); - -this.allData = JSON.parse(dataViewToText(result)); // parse json data and pass json string - - - - -this.PM25= this.allData['P25']; - - -//outdoor -if (Number(this.PM25)>0 && Number(this.PM25)<25 ) this.PM25Interpret= ("Good"); -if (Number(this.PM25)>24 && Number(this.PM25)<51 )this.PM25Interpret= ("Fair"); -if (Number(this.PM25)>49 && Number(this.PM25)<101 )this.PM25Interpret= ("Poor"); -if (Number(this.PM25)>100 && Number(this.PM25)<300 )this.PM25Interpret= ("Very Poor"); -if (Number(this.PM25)>300 ) this.PM25Interpret= ("Extremely Poor"); - - - -this.TEMP= this.allData['tmp']; -this.HUMID= this.allData['hum']; -this.PAX= this.allData['PAX']; -this.CO2= this.allData['CO2']; - -// lets assume we have -if (Number(this.CO2)>0) { -this.TEMP= this.allData['CO2T']; -this.HUMID= this.allData['CO2H']; - -} - - - - - - -let d = new Date(); -this.datetime=d; -var unixTimeStamp = Math.floor(d.getTime() / 1000); -this.datetime_ux=unixTimeStamp.toString(); -this.output_json=dataViewToText(result); -this.txtpm25=this.output_json; -this.rec_uid = Guid.raw(); // make it a string - -var Comment = Parse.Object.extend('canairio_raw_data'); -var canairio_store = new Comment(); // set initial data record canairio_store.set('session_UID',this.session_uid); canairio_store.set('record_UID',this.rec_uid); canairio_store.set('device_UID',this.deviceId); canairio_store.set('output_json',dataViewToText(result)); -canairio_store.set('PM25',this.PM25); canairio_store.set('TEMP',this.TEMP); canairio_store.set('HUMID',this.HUMID); canairio_store.set('CO2',this.CO2); canairio_store.set('PAX',this.PAX); - canairio_store.set('latitude',this.latitude); canairio_store.set('longitude',this.longitude); canairio_store.set('altitude',this.altitude); + canairio_store.set('datetime_ux',this.datetime_ux); -await canairio_store.save(); +canairio_store.set('user_uid', this.user.uid); +canairio_store.set('name', this.user.displayName); +canairio_store.set('email', this.user.email); +//log the parse save to console +canairio_store.save().then((saveResult: any) => { + console.log(saveResult); -} +}, (error: any) => { + console.log(error); +}); + } - - - - - - - async getLocation() { - const position = await Geolocation.getCurrentPosition({enableHighAccuracy: true}); - this.latitude = position.coords.latitude; - console.log (position.coords.latitude); - this.longitude = position.coords.longitude; - this.altitude = position.coords.altitude; - return position.coords; -} - - - private parseInitialize() { - - Parse.initialize(this.parseAppId, this.parseJSKey); - - (Parse as any).serverURL = this.ParseServerURL; // use your server url - - } - - - async connect(){ - - try { - await BleClient.initialize(); - const device = await BleClient.requestDevice({ - namePrefix: 'CanAirIO', - optionalServices : [PM25_SERVICE_LCASE] + async getLocation() { + const position = await Geolocation.getCurrentPosition({ + enableHighAccuracy: true, }); - - // console.log('device', device); - - await BleClient.connect(device.deviceId); - console.log('connected to device', device); - - this.deviceId=device.deviceId; -this.readandSave(); - - - setTimeout(async () => { - await BleClient.stopLEScan(); - console.log('stopped scanning'); - }, 5000); - } catch (error) { - console.error(error); + this.latitude = position.coords.latitude; + console.log(position.coords.latitude); + this.longitude = position.coords.longitude; + this.altitude = position.coords.altitude; + return position.coords; } -} + private parseInitialize() { + + Parse.initialize(ENV.parseAppId, ENV.parseJSKey); + (Parse as any).serverURL = ENV.parseServerUrl; // use your server url + + } + async connect() { + try { + await BleClient.initialize(); + const device = await BleClient.requestDevice({ + namePrefix: 'CanAirIO', + optionalServices: [PM25_SERVICE_LCASE], + }); + + // console.log('device', device); + + await BleClient.connect(device.deviceId); + console.log('connected to device', device); + + this.deviceId = device.deviceId; + this.readandSave(); + + setTimeout(async () => { + await BleClient.stopLEScan(); + console.log('stopped scanning'); + }, 5000); + } catch (error) { + console.error(error); + } + } } diff --git a/src/app/mobisplugins/ispex/classify/classify-routing.module.ts b/src/app/mobisplugins/ispex/classify/classify-routing.module.ts new file mode 100644 index 0000000..6c9730e --- /dev/null +++ b/src/app/mobisplugins/ispex/classify/classify-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { ClassifyPage } from './classify.page'; + +const routes: Routes = [ + { + path: '', + component: ClassifyPage + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class ClassifyPageRoutingModule {} diff --git a/src/app/mobisplugins/ispex/classify/classify.module.ts b/src/app/mobisplugins/ispex/classify/classify.module.ts new file mode 100644 index 0000000..bc9be35 --- /dev/null +++ b/src/app/mobisplugins/ispex/classify/classify.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { ClassifyPageRoutingModule } from './classify-routing.module'; + +import { ClassifyPage } from './classify.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + ClassifyPageRoutingModule + ], + declarations: [ClassifyPage] +}) +export class ClassifyPageModule {} diff --git a/src/app/mobisplugins/ispex/classify/classify.page.html b/src/app/mobisplugins/ispex/classify/classify.page.html new file mode 100644 index 0000000..0a517cb --- /dev/null +++ b/src/app/mobisplugins/ispex/classify/classify.page.html @@ -0,0 +1,55 @@ + + + Classify your spectrum + + + + + + + + + + + + + + + + + + + + Choose a class + + + + + +Gray card + + +Sky + + +Water + + + +Lamp + + + +Other + + + + + + + + + + + + diff --git a/src/app/mobisplugins/ispex/classify/classify.page.scss b/src/app/mobisplugins/ispex/classify/classify.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/mobisplugins/ispex/classify/classify.page.spec.ts b/src/app/mobisplugins/ispex/classify/classify.page.spec.ts new file mode 100644 index 0000000..ebc5658 --- /dev/null +++ b/src/app/mobisplugins/ispex/classify/classify.page.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ClassifyPage } from './classify.page'; + +describe('ClassifyPage', () => { + let component: ClassifyPage; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ ClassifyPage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(ClassifyPage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/mobisplugins/ispex/classify/classify.page.ts b/src/app/mobisplugins/ispex/classify/classify.page.ts new file mode 100644 index 0000000..9f82215 --- /dev/null +++ b/src/app/mobisplugins/ispex/classify/classify.page.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-classify', + templateUrl: './classify.page.html', + styleUrls: ['./classify.page.scss'], +}) +export class ClassifyPage implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/mobisplugins/ispex/ispex-routing.module.ts b/src/app/mobisplugins/ispex/ispex-routing.module.ts index 22814d6..9b80f75 100644 --- a/src/app/mobisplugins/ispex/ispex-routing.module.ts +++ b/src/app/mobisplugins/ispex/ispex-routing.module.ts @@ -7,6 +7,10 @@ const routes: Routes = [ { path: '', component: IspexPage + }, + { + path: 'classify', + loadChildren: () => import('./classify/classify.module').then( m => m.ClassifyPageModule) } ]; diff --git a/src/app/mobisplugins/ispex/ispex.module.ts b/src/app/mobisplugins/ispex/ispex.module.ts index 0f72f06..f79a007 100644 --- a/src/app/mobisplugins/ispex/ispex.module.ts +++ b/src/app/mobisplugins/ispex/ispex.module.ts @@ -1,24 +1,24 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; - import { IonicModule } from '@ionic/angular'; - import { IspexPageRoutingModule } from './ispex-routing.module'; -import { PreviewPageModule } from "./preview/preview.module" - -import { Vibration } from '@ionic-native/vibration/ngx'; - +import { TranslateModule } from '@ngx-translate/core'; import { IspexPage } from './ispex.page'; +import { PreviewPage } from './preview/preview.page'; +import { PreviewPageModule } from "./preview/preview.module" @NgModule({ imports: [ CommonModule, FormsModule, IonicModule, - IspexPageRoutingModule + IspexPageRoutingModule, + TranslateModule, + PreviewPageModule + ], declarations: [IspexPage], - providers: [Vibration] + }) export class IspexPageModule {} diff --git a/src/app/mobisplugins/ispex/ispex.page.html b/src/app/mobisplugins/ispex/ispex.page.html index d994a9d..ce9594e 100644 --- a/src/app/mobisplugins/ispex/ispex.page.html +++ b/src/app/mobisplugins/ispex/ispex.page.html @@ -1,64 +1,32 @@ - - - New iSPEX observation - + + - - - + + +Snap a spectrum + + + +Connect your iSPEX unit to your phone and snap a spectrum. + -
- +
+
+ + Lets classify this spectrum! + +
+ + - Attach the iSPEX unit and open camera + Snap a spectrum - - - - Mount iSPEX2 unit on phone - - 1. If you have a smartphone - cover or case, remove it.
- 2. Take your smartphone in - one hand and the iSPEX2 - add-on in the other. If this - is not possible, use a table - or similar surface to lay the - smartphone on.
- - 3. The iSPEX2 add-on will be mounted on the back side of the phone and the clip will be on the front screen. - Slide your smartphone into the smartphone clip. You may need to use your other - hand to slightly open the clip, especially for thicker - smartphones.
- - 4. Slide the iSPEX2 - add-on over your - smartphone until the - bars on the top and - left sides fit snugly.
- - 5. Firmly press the suction cup on the - back of the smartphone until it is - clearly attached.
- - 6. Gently move the smartphone back - and forth to ensure the add-on is - firmly attached. - If the add-on appears to rotate or slide off, start again.
-
- - - - - - -
\ No newline at end of file diff --git a/src/app/mobisplugins/ispex/ispex.page.ts b/src/app/mobisplugins/ispex/ispex.page.ts index 09db239..2b392b3 100644 --- a/src/app/mobisplugins/ispex/ispex.page.ts +++ b/src/app/mobisplugins/ispex/ispex.page.ts @@ -1,75 +1,100 @@ -import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; +/* eslint-disable @typescript-eslint/naming-convention */ +import * as Parse from 'parse'; +import { ENV } from '../../app.constant'; +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import { ModalController } from '@ionic/angular'; import { PreviewPage } from './preview/preview.page'; - -declare var cv: any; +import { TranslateService } from '@ngx-translate/core'; +import { AuthService } from '../../services/auth.service'; +import { Auth, user } from '@angular/fire/auth'; +import { Storage } from '@ionic/storage-angular'; +import { Router } from '@angular/router'; + +Parse.initialize(ENV.parseAppId, ENV.parseJSKey); +(Parse as any).serverURL = ENV.parseServerUrl; // use your server url + +let uploadSuccess = false; @Component({ selector: 'app-ispex', templateUrl: './ispex.page.html', styleUrls: ['./ispex.page.scss'], }) +export class IspexPage implements OnInit { + uploadSuccess = false; // Add this line + image = ''; + user = null; + language = ''; - -export class IspexPage implements AfterViewInit { - - @ViewChild('imageCanvas', { static: false }) canvasEl : ElementRef; - - @ViewChild('image', { static: false }) imageEl : ElementRef; - - image = null; - constructor(private modal: ModalController) {} - - ngAfterViewInit() { - - -// this._CANVAS = this.canvasEl.nativeElement; -// this._IMAGE = this.imageEl.nativeElement; - -console.log ("ngAfterViewInit"); - - + constructor( + private modal: ModalController, + private translateService: TranslateService, + private authService: AuthService, + private afAuth: Auth, + private storage: Storage, + private router: Router + ) { + user(this.afAuth).subscribe((response) => { + //fill the user to verify if someone is logged in + this.user = response; + console.log(this.user.uid); + }); } + ngOnInit() {} async openCamera() { const modal = await this.modal.create({ component: PreviewPage, cssClass: 'fullscreen', - animated: true + animated: true, }); modal.onDidDismiss().then((data) => { - if (data !== null) { - this.image = data.data; + if (data !== null) { + this.image = data.data; + this.uploadSuccess = true; + // create parse class - let imgElement = document.getElementById('img'); -let src = cv.imread(imgElement); -let dst = new cv.Mat(); -let dsize = new cv.Size(src.rows, src.cols); -let center = new cv.Point(src.cols / 2, src.rows / 2); -let M = cv.getRotationMatrix2D(center, -90, 1); -cv.warpAffine(src, dst, M, dsize, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar()); -cv.imshow('canvasOutput', dst); -src.delete(); dst.delete(); M.delete(); + const myspex_data_store = Parse.Object.extend('myspex_data'); + // create new instance of parse class - cv.imshow('canvasOutput', dst); - src.delete(); dst.delete(); + const myspex_data = new myspex_data_store(); + // set value for parse clas + const file = new Parse.File('image.jpg', { base64: this.image }); + file.save().then( + (file) => { + console.log(file); + }, + (error) => { + console.log(error); + } + ); + myspex_data.set('user_uid', this.user.uid); + myspex_data.set('name', this.user.displayName); + myspex_data.set('email', this.user.email); + myspex_data.set('image', file); - } + myspex_data.save().then( + (result: any) => { + console.log(result); + }, + (error: any) => { + console.log(error); + } + ); + } else { + console.log('no data'); + } }); return await modal.present(); } - - - - - -} \ No newline at end of file + // navigate to the classify page +} diff --git a/src/app/mobisplugins/ispex/preview/preview.page.html b/src/app/mobisplugins/ispex/preview/preview.page.html index 891dd09..da92b16 100644 --- a/src/app/mobisplugins/ispex/preview/preview.page.html +++ b/src/app/mobisplugins/ispex/preview/preview.page.html @@ -1,32 +1,13 @@ - - - -
- - - - -X - - - - Grey - - - Sky - - - Water + - - Free + + -
-
\ No newline at end of file +
diff --git a/src/app/mobisplugins/ispex/preview/preview.page.scss b/src/app/mobisplugins/ispex/preview/preview.page.scss index 9b08bb5..c883faf 100644 --- a/src/app/mobisplugins/ispex/preview/preview.page.scss +++ b/src/app/mobisplugins/ispex/preview/preview.page.scss @@ -1,65 +1,42 @@ +ion-content { + --background: transparent !important; +} + +#capture { + position: absolute; + bottom: 30px; + left: calc(50% - 25px); + width: 50px; + height: 50px; + z-index: 99999; +} + +#flip { + position: absolute; + bottom: 30px; + left: calc(50% + 125px); + width: 50px; + height: 50px; + z-index: 99999; +} #close { position: absolute; bottom: 30px; - left: calc(50% - 160px); + left: calc(50% - 175px); width: 50px; height: 50px; z-index: 99999; } -#rgb { - position: absolute; - bottom: 30px; - left: calc(50% - 140px); - width: 50px; - height: 50px; - z-index: 99999; +#capture::part(native) { + border-radius: 30px; } - - - -#grey { - position: absolute; - bottom: 30px; - left: calc(50% - 80px); - width: 50px; - height: 50px; - z-index: 99999; +#close::part(native) { + border-radius: 30px; } - - -#sky { - position: absolute; - bottom: 30px; - left: calc(50% - 20px); - width: 50px; - height: 50px; - z-index: 99999; +#flip::part(native) { + border-radius: 30px; } - - -#water{ - position: absolute; - bottom: 30px; - left: calc(50% + 40px); - width: 50px; - height: 50px; - z-index: 99999; -} - - -#free { - position: absolute; - bottom: 30px; - left: calc(50% + 100px); - width: 50px; - height: 50px; - z-index: 99999; -} - -ion-content { --background: black;} - - diff --git a/src/app/mobisplugins/ispex/preview/preview.page.ts b/src/app/mobisplugins/ispex/preview/preview.page.ts index 5141ddc..8eda050 100644 --- a/src/app/mobisplugins/ispex/preview/preview.page.ts +++ b/src/app/mobisplugins/ispex/preview/preview.page.ts @@ -1,15 +1,9 @@ -import * as Parse from 'parse'; -import { ENV } from '../../../app.constant'; -import { Vibration } from '@ionic-native/vibration/ngx'; import { Component, OnInit } from '@angular/core'; import { Plugins } from "@capacitor/core" const { CameraPreview } = Plugins; -import { CameraPreviewOptions, CameraPreviewPictureOptions, CameraSampleOptions } from '@capacitor-community/camera-preview'; +import { CameraPreviewOptions, CameraPreviewPictureOptions } from '@capacitor-community/camera-preview'; +import '@capacitor-community/camera-preview'; import { ModalController } from '@ionic/angular'; -import { Geolocation} from '@capacitor/geolocation'; - -import { Device } from '@capacitor/device'; - @Component({ selector: 'app-preview', @@ -17,243 +11,46 @@ import { Device } from '@capacitor/device'; styleUrls: ['./preview.page.scss'], }) export class PreviewPage implements OnInit { - image = null; + image =""; cameraActive = false; -private parseAppId: string = ENV.parseAppId; -private parseServerUrl: string = ENV.parseServerUrl; -private parseJSKey: string=ENV.parseJSKey; -public datetime_ux: string; -public datetime: string; - -public latitude: number; -public longitude: number; -public altitude: number; -public heading: number; - -// public deviceAppVersion: string; -// public deviceAppBuild: string; -public deviceOsVersion: string; -public devicePlatform: string; - -public deviceManufacturer: string; -public deviceModel: string; - - - - - - - - - - constructor(private modal: ModalController, private vibration: Vibration -) { } + constructor(private modal: ModalController) { } ngOnInit() { - - - this.getLocation(); - this.parseInitialize(); this.launchCamera(); + } -this.getDeviceInfo(); - -} - - launchCamera() { - const cameraPreviewOptions: CameraPreviewOptions = { - position: 'rear', // front or rear + const cameraPreviewOptions: CameraPreviewOptions = { + position: 'front', // front or rear parent: 'content', // the id on the ion-content className: '', width: window.screen.width, //width of the camera display - height:window.screen.height-200, //height of the camera - + height: window.screen.height - 200, //height of the camera toBack: false, disableAudio: true, - rotateWhenOrientationChanged: false, - lockAndroidOrientation: true - - + enableHighResolution: true, }; - CameraPreview.start(cameraPreviewOptions); + CameraPreview['start'](cameraPreviewOptions); this.cameraActive = true; - - - - - } - - - - - - async takePicture( dngType ) { - - -// buzz for user feedback - - this.vibration.vibrate(1); - - - // this captures a raw image. replace quality with type + async takePicture() { const cameraPreviewPictureOptions: CameraPreviewPictureOptions = { - quality: dngType// misuse quality: 0 normal (rgb) image 1 gray card 2 sky image 3 water image 4 other spectro(pol) + quality: 90 }; - - - const result = await CameraPreview.capture(cameraPreviewPictureOptions); + const result = await CameraPreview['capture'](cameraPreviewPictureOptions); this.image = `data:image/jpeg;base64,${result.value}`; - - -this.getLocation(); - - // save to parse - - -const base64PictureData = result.value; - - -let d = new Date(); -this.datetime=d.toString(); -var unixTimeStamp = Math.floor(d.getTime() / 1000); -this.datetime_ux=unixTimeStamp.toString(); - -// do something with base64PictureData - -var Base64SnapImage:string; - -Base64SnapImage = "data:image/jpeg;base64," + base64PictureData; - - -var Base64DNGImage:string; - - - - - -var Save2Parse = Parse.Object.extend('ispex_data'); - - - - - -var iSPEX2_store = new Save2Parse(); -// set initial data record - - -// upload thumb for user feedback. DNG's are uploaded within the native part - - -const thumbFiletoUpload = new Parse.File("thumb.jpg", { base64: Base64SnapImage},"image/jpeg"); - - thumbFiletoUpload.save().then(function() { - console.log ("saved thumb!") - }, function(error) { - // The file either could not be read, or could not be saved to Parse. - - console.log ("error saving thumb") - - }); - - - - - - -iSPEX2_store.set('thumb', thumbFiletoUpload); - -iSPEX2_store.set('latitude',this.latitude); -iSPEX2_store.set('longitude',this.longitude); -iSPEX2_store.set('altitude',this.altitude); -iSPEX2_store.set('heading',this.heading); - -iSPEX2_store.set('dngFileURL',result.dngFileURL); -iSPEX2_store.set('heading',result.heading); -iSPEX2_store.set('device_angle',result.device_angle); - - -iSPEX2_store.set ('device_osversion', this.deviceOsVersion); -iSPEX2_store.set ('device_platform', this.devicePlatform); -iSPEX2_store.set ('device_manufacturer', this.deviceManufacturer); -iSPEX2_store.set ('device_model', this.deviceModel); - - -iSPEX2_store.set ('datetimerecorded', this.datetime); -iSPEX2_store.set ('datetime_ux', this.datetime_ux); - - - - -await iSPEX2_store.save(); - - - this.stopCamera(); } - - - - - - - - - - async getLocation() { - const position = await Geolocation.getCurrentPosition({enableHighAccuracy: true}); - this.latitude = position.coords.latitude; - this.heading= position.coords.heading; - // console.log (position.coords.latitude); - this.longitude = position.coords.longitude; - this.altitude = position.coords.altitude; - return position.coords; -} - - - async takeSample() { - - - - } - - async stopCamera() { - await CameraPreview.stop(); + await CameraPreview['stop'](); this.modal.dismiss(this.image); } async flipCamera() { - await CameraPreview.flip(); + await CameraPreview['flip'](); } - async getDeviceInfo() { - const info = await Device.getInfo(); - // this.deviceAppVersion = info.appVersion; - // this.deviceAppBuild = info.appBuild; - this.deviceOsVersion = info.osVersion; - this.devicePlatform = info.platform; - this.deviceManufacturer = info.manufacturer; - this.deviceModel = info.model; - console.log(info); - } - - - - - private parseInitialize() { - - Parse.initialize(this.parseAppId, this.parseJSKey); - - (Parse as any).serverURL = this.parseServerUrl; // use your server url - - } - - - - -} \ No newline at end of file +} diff --git a/src/app/mobisplugins/minisecchi/instructions/instructions.page.html b/src/app/mobisplugins/minisecchi/instructions/instructions.page.html index 674667d..992acf0 100644 --- a/src/app/mobisplugins/minisecchi/instructions/instructions.page.html +++ b/src/app/mobisplugins/minisecchi/instructions/instructions.page.html @@ -1,14 +1,13 @@ - - - Prepare - - - - - + + + + + + PREPARE + Prepare the device @@ -45,7 +44,7 @@ Attach (optional) pH Strip 4. Unlock spindle - + @@ -56,7 +55,7 @@ Attach (optional) pH Strip - + Step 2 diff --git a/src/app/mobisplugins/minisecchi/instructions/step2/step2.page.html b/src/app/mobisplugins/minisecchi/instructions/step2/step2.page.html index b71b553..048fc2c 100644 --- a/src/app/mobisplugins/minisecchi/instructions/step2/step2.page.html +++ b/src/app/mobisplugins/minisecchi/instructions/step2/step2.page.html @@ -1,11 +1,9 @@ - - - 2. Transperancy - - - - - + + + + + + 2. Transparancy @@ -14,7 +12,7 @@ -Turning the spindle, lower the disk until it disappears from view, then raise the disk until it re-appears. Record the re-appearance distance. +Turning the spindle, lower the disk until it disappears from view, then raise the disk until it re-appears. Record the re-appearance distance. @@ -32,7 +30,7 @@ The app will now calculate the Secchi disk depth (re-appareance distance minus d - + Step 3 diff --git a/src/app/mobisplugins/minisecchi/instructions/step3/step3.page.html b/src/app/mobisplugins/minisecchi/instructions/step3/step3.page.html index 3596741..bdd7851 100644 --- a/src/app/mobisplugins/minisecchi/instructions/step3/step3.page.html +++ b/src/app/mobisplugins/minisecchi/instructions/step3/step3.page.html @@ -1,11 +1,9 @@ - - - 3. Disk color - - - - - + + + + + + 3. Disk color @@ -22,7 +20,7 @@ The app will now calculate the Secchi disk depth (re-appareance distance minus d - + Step 4 diff --git a/src/app/mobisplugins/minisecchi/instructions/step4/step4.page.html b/src/app/mobisplugins/minisecchi/instructions/step4/step4.page.html index 67b522e..8c47ed8 100644 --- a/src/app/mobisplugins/minisecchi/instructions/step4/step4.page.html +++ b/src/app/mobisplugins/minisecchi/instructions/step4/step4.page.html @@ -1,18 +1,14 @@ - - - 4. Color of the water - - - - + + + + + + 4. Color of the water - - - - + Colour of the water surface Estimate the Forel-Ule colour scale by looking at the water surface, away from sun glitter. @@ -26,7 +22,7 @@ Estimate the Forel-Ule colour scale by looking at the water surface, away from s Keeping the sun diagonally behind you over your left or right shoulder, look down and forward to the water surface (40-45 degrees) and compare the colour of the water to the Forel-Ule index. Optionally upload another photo using the same viewing angles.
- + Step 5 diff --git a/src/app/mobisplugins/minisecchi/instructions/step5/step5.page.html b/src/app/mobisplugins/minisecchi/instructions/step5/step5.page.html index fcdde98..7b0104d 100644 --- a/src/app/mobisplugins/minisecchi/instructions/step5/step5.page.html +++ b/src/app/mobisplugins/minisecchi/instructions/step5/step5.page.html @@ -1,12 +1,10 @@ - - - 5. Additional - - - - + + + + + + 5. Additional - @@ -15,7 +13,7 @@ pH (acidity) -If you deployed the mini-Secchi disk with pH paper, compare the colour of the pH paper to the pH colour scale. +If you deployed the mini-Secchi disk with pH paper, compare the colour of the pH paper to the pH colour scale. Natural waters are usually only slightly acidic (6) or alkalic (8). The acidity of the water gives an indication of how much Carbon is dissolved in the water and whether algae or plants are likely to grow. @@ -27,6 +25,6 @@ Natural waters are usually only slightly acidic (6) or alkalic (8). The acidity - Finish + Finish diff --git a/src/app/mobisplugins/minisecchi/measure/colourathalfdepth/colourathalfdepth.page.html b/src/app/mobisplugins/minisecchi/measure/colourathalfdepth/colourathalfdepth.page.html index 818a448..fae6822 100644 --- a/src/app/mobisplugins/minisecchi/measure/colourathalfdepth/colourathalfdepth.page.html +++ b/src/app/mobisplugins/minisecchi/measure/colourathalfdepth/colourathalfdepth.page.html @@ -1,11 +1,9 @@ - - - Colour at half depth - - - - - + + + + + + Colour at half depth @@ -50,4 +48,4 @@ The colour of the disk in the water is most similar to Forel Ule colour: Go to Step 4 -
\ No newline at end of file +
diff --git a/src/app/mobisplugins/minisecchi/measure/colouratsurface/colouratsurface.page.html b/src/app/mobisplugins/minisecchi/measure/colouratsurface/colouratsurface.page.html index cff30f3..f75747f 100644 --- a/src/app/mobisplugins/minisecchi/measure/colouratsurface/colouratsurface.page.html +++ b/src/app/mobisplugins/minisecchi/measure/colouratsurface/colouratsurface.page.html @@ -1,120 +1,127 @@ - - - Additional - - - - - + + + + + + Additional + + You can now wind up the + Secchi disk mechanism. + -You can now wind up the Secchi disk mechanism. + + Acidity + - - -Acidity - + + If you used pH paper, compare the colour of the pH strip to the pH colour + index provided. + +
+ + The pH value is (1-14): + + + + + + Surface water colour + + + You can upload a photo of the colour of the water, avoiding reflections from + the sun: Hold the camera 40-45 degrees from horizontal towards the water + surface. With the sun straight at your back rotate 45 degrees to your left + or your right to have the sun diagonally over your left or right shoulder. + - -If you used pH paper, compare the colour of the pH strip to the pH colour index provided. - - +
-
-
- - - - - - The pH value is (1-14): - - - - - - - - - - - - - - - - -Surface water colour - - - -You can upload a photo of the colour of the water, avoiding reflections from the sun: -Hold the camera 40-45 degrees from horizontal towards the water surface. -With the sun straight at your back rotate 45 degrees to your left or your right to have the sun diagonally over your left or right shoulder. - - - - -
-
- - - -To add a photo click here: + + To add a photo click here: - - + + + + Looking at the water surface in this direction, the colour of the water + is
+ most similar to Forel-Ule colour number (1-21): +
+
+ + + + Quality control check-list + + + + The bottom was visible / the disk reached the bottom while I could still + see it. + + +   + I reached the end of the tape while I could still see the disk + + + + I was not able to keep the disk going down at a straight angle. The + estimated distance angle is: + + + degrees from vertical. + - - -Looking at the water surface in this direction, the colour of the water is
most similar to Forel-Ule colour number (1-21): -
-
- - - - - -Quality control check-list - - - - - -The bottom was visible / the disk reached the bottom while I could still see it. - - - - -  I reached the end of the tape while I could still see the disk - - - - -I was not able to keep the disk going down at a straight angle. The estimated distance angle is: - - - -degrees from vertical. - - - -Go to step 5 - - + + Go to step 5 +
diff --git a/src/app/mobisplugins/minisecchi/measure/distancetowater/distancetowater.page.html b/src/app/mobisplugins/minisecchi/measure/distancetowater/distancetowater.page.html index eb81103..c06019f 100644 --- a/src/app/mobisplugins/minisecchi/measure/distancetowater/distancetowater.page.html +++ b/src/app/mobisplugins/minisecchi/measure/distancetowater/distancetowater.page.html @@ -1,14 +1,11 @@ - - - Distance to water - - - - - + + + + + + Distance to water - Wind the disk down to the water surface and record the distance from the handheld device to the water surface. Keep your arm at the same distance from the water for the remaining depth measurements. diff --git a/src/app/mobisplugins/minisecchi/measure/measure.page.html b/src/app/mobisplugins/minisecchi/measure/measure.page.html index 1d55e92..0e71ddc 100644 --- a/src/app/mobisplugins/minisecchi/measure/measure.page.html +++ b/src/app/mobisplugins/minisecchi/measure/measure.page.html @@ -1,40 +1,39 @@ - - - Safety first - - - - - + + + + + + Safety first - - - -Safety first + + Safety first + + + Work from a stable platform, wear a life vest and make sure someone knows + where you are. Only upload photos if you can do so without risking your + safety and your phone. Find a spot where you can look into the water, away + from sun reflections. Record Secchi depth, water colour and pH without + sunglasses. +
+ If you have pH paper, clip a new section into the provided clip and attach + it to the disk.
- +
+ + + -Work from a stable platform, wear a life vest and make sure someone knows where you are. Only upload photos if you can do so without risking your safety and your phone. -Find a spot where you can look into the water, away from sun reflections. Record Secchi depth, water colour and pH without sunglasses. -
-If you have pH paper, clip a new section into the provided clip and attach it to the disk. - -
- -
- - - - - - - - Go to step 1 - - + + Go to step 1 +
diff --git a/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.html b/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.html index 52fc189..765ba59 100644 --- a/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.html +++ b/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.html @@ -1,11 +1,9 @@ - - - Review - - - - - + + + + + + Review diff --git a/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.ts b/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.ts index 3f38abc..0c4990d 100644 --- a/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.ts +++ b/src/app/mobisplugins/minisecchi/measure/qccheck/qccheck.page.ts @@ -4,6 +4,9 @@ import { Guid } from "guid-typescript"; import * as Parse from 'parse'; import { ENV } from '../../../../app.constant'; import { Geolocation } from '@capacitor/geolocation'; +import { AuthService } from '../../../../services/auth.service'; +import { Auth, user } from '@angular/fire/auth'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-qccheck', @@ -15,7 +18,8 @@ export class QccheckPage implements OnInit { private parseAppId: string = ENV.parseAppId; private parseServerUrl: string = ENV.parseServerUrl; private parseJSKey: string = ENV.parseJSKey; - + user = null; + language= ''; public swversion_number: string; public swversion_code: number; public latitude: number; @@ -39,8 +43,14 @@ export class QccheckPage implements OnInit { newSecchi = { uid: null, swversion_number: null, swversion_code: null, latitude: null, longitude: null, distancetowater: null, reappear: null, colourathalfdepth: null, colourathalfdepthimage: null, colouratsurface: null, colouratsurfaceimage: null, datetimerecorded: null, datetime_ux: null, bottom_visible: null, end_of_tape: null, phvalue: null, angle_estimated: null, secchi_depth: null }; - constructor(private storage: Storage) { - + constructor(private storage: Storage, private translateService: TranslateService, + private authService: AuthService, + private afAuth: Auth) { + user(this.afAuth).subscribe((response) => { + //fill the user to verify if someone is logged in + this.user = response; + console.log (this.user.uid) + }); this.rec_uid = Guid.raw(); // make it a string } @@ -235,7 +245,9 @@ export class QccheckPage implements OnInit { secchi_store.set('secchi_depth', this.secchi_depth); secchi_store.set('datetime_ux', this.datetime_ux.toString()); secchi_store.set('datetimerecorded', this.datetime.toISOString()); - + secchi_store.set('user_uid', this.user.uid); + secchi_store.set('name', this.user.displayName); + secchi_store.set('email', this.user.email); diff --git a/src/app/mobisplugins/minisecchi/measure/reappear/reappear.page.html b/src/app/mobisplugins/minisecchi/measure/reappear/reappear.page.html index 9df18c6..64f0778 100644 --- a/src/app/mobisplugins/minisecchi/measure/reappear/reappear.page.html +++ b/src/app/mobisplugins/minisecchi/measure/reappear/reappear.page.html @@ -1,11 +1,9 @@ - - - Disk reappearance - - - - - + + + + + + Disk reappearance diff --git a/src/app/mobisplugins/minisecchi/minisecchi.page.html b/src/app/mobisplugins/minisecchi/minisecchi.page.html index 34997b4..d2aa884 100644 --- a/src/app/mobisplugins/minisecchi/minisecchi.page.html +++ b/src/app/mobisplugins/minisecchi/minisecchi.page.html @@ -1,33 +1,61 @@ - - New Mini Secchi observation + + Mini Secchi - - - + + Mini Secchi + +
+ +
- -
-
- - - New observation - - - - Instructions - - - - + + New observation + - - - -
- \ No newline at end of file + + Instructions + +
diff --git a/src/app/mobisplugins/plantnet/classify/classify-routing.module.ts b/src/app/mobisplugins/plantnet/classify/classify-routing.module.ts new file mode 100644 index 0000000..6c9730e --- /dev/null +++ b/src/app/mobisplugins/plantnet/classify/classify-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { ClassifyPage } from './classify.page'; + +const routes: Routes = [ + { + path: '', + component: ClassifyPage + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class ClassifyPageRoutingModule {} diff --git a/src/app/mobisplugins/plantnet/classify/classify.module.ts b/src/app/mobisplugins/plantnet/classify/classify.module.ts new file mode 100644 index 0000000..bc9be35 --- /dev/null +++ b/src/app/mobisplugins/plantnet/classify/classify.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { ClassifyPageRoutingModule } from './classify-routing.module'; + +import { ClassifyPage } from './classify.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + ClassifyPageRoutingModule + ], + declarations: [ClassifyPage] +}) +export class ClassifyPageModule {} diff --git a/src/app/mobisplugins/plantnet/classify/classify.page.html b/src/app/mobisplugins/plantnet/classify/classify.page.html new file mode 100644 index 0000000..6b0c137 --- /dev/null +++ b/src/app/mobisplugins/plantnet/classify/classify.page.html @@ -0,0 +1,55 @@ + + + Classify your image + + + + + + + + + + + + + + + + + + + + Choose a class + + + + + + Leaf + + + +Flower + + +Fruit + + + +Bark + + +Automatic + + + + + + + + + + + + diff --git a/src/app/mobisplugins/plantnet/classify/classify.page.scss b/src/app/mobisplugins/plantnet/classify/classify.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/mobisplugins/plantnet/classify/classify.page.spec.ts b/src/app/mobisplugins/plantnet/classify/classify.page.spec.ts new file mode 100644 index 0000000..ebc5658 --- /dev/null +++ b/src/app/mobisplugins/plantnet/classify/classify.page.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ClassifyPage } from './classify.page'; + +describe('ClassifyPage', () => { + let component: ClassifyPage; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ ClassifyPage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(ClassifyPage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/mobisplugins/plantnet/classify/classify.page.ts b/src/app/mobisplugins/plantnet/classify/classify.page.ts new file mode 100644 index 0000000..ea17a33 --- /dev/null +++ b/src/app/mobisplugins/plantnet/classify/classify.page.ts @@ -0,0 +1,56 @@ +import { Component, OnInit } from '@angular/core'; +import {ENV} from '../../../app.constant'; + +@Component({ + selector: 'app-classify', + templateUrl: './classify.page.html', + styleUrls: ['./classify.page.scss'], +}) +export class ClassifyPage implements OnInit { + PROJECT = 'all'; // try 'weurope' or 'canada' + API_URL = 'https://my-api.plantnet.org/v2/identify/' + this.PROJECT + '?api-key='; + API_PRIVATE_KEY = ENV.plantnetKey; // secret + API_SIMSEARCH_OPTION = '&include-related-images=true'; // optional: get most similar images + API_LANG = '&lang=fr'; // default: en + + IMAGE_1 = '../data/image_1.jpeg'; + ORGAN_1 = 'flower'; + IMAGE_2 = '../data/image_2.jpeg'; + ORGAN_2 = 'leaf'; + + constructor() { } + + ngOnInit() { + +// now make a POST request to the API +const xhr = new XMLHttpRequest(); +xhr.open('POST', this.API_URL + this.API_PRIVATE_KEY + this.API_SIMSEARCH_OPTION + this.API_LANG, true); +xhr.setRequestHeader('Content-Type', 'application/json'); +xhr.onload = function () { + // do something to response + console.log(this.responseText); +}; +xhr.send(JSON.stringify({ + images: [ + { + url: this.IMAGE_1, + organ: this.ORGAN_1 + }, + { + url: this.IMAGE_2, + organ: this.ORGAN_2 + + } + ] +})); + + + + + + + + + } + +} diff --git a/src/app/mobisplugins/plantnet/plantnet-routing.module.ts b/src/app/mobisplugins/plantnet/plantnet-routing.module.ts index cc44f85..bb82488 100644 --- a/src/app/mobisplugins/plantnet/plantnet-routing.module.ts +++ b/src/app/mobisplugins/plantnet/plantnet-routing.module.ts @@ -7,6 +7,10 @@ const routes: Routes = [ { path: '', component: PlantnetPage + }, + { + path: 'classify', + loadChildren: () => import('./classify/classify.module').then( m => m.ClassifyPageModule) } ]; diff --git a/src/app/mobisplugins/plantnet/plantnet.module.ts b/src/app/mobisplugins/plantnet/plantnet.module.ts index 19497ec..1799455 100644 --- a/src/app/mobisplugins/plantnet/plantnet.module.ts +++ b/src/app/mobisplugins/plantnet/plantnet.module.ts @@ -1,29 +1,24 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; - import { IonicModule } from '@ionic/angular'; - import { PlantnetPageRoutingModule } from './plantnet-routing.module'; - +import { TranslateModule } from '@ngx-translate/core'; import { PlantnetPage } from './plantnet.page'; - - -import { Component, OnInit } from '@angular/core'; -import { MenuController } from '@ionic/angular'; -import { TranslateService } from '@ngx-translate/core'; -import { Geolocation, PositionOptions } from '@capacitor/geolocation'; - - - +import { PreviewPage } from './preview/preview.page'; +import { PreviewPageModule } from "./preview/preview.module" @NgModule({ imports: [ CommonModule, FormsModule, IonicModule, - PlantnetPageRoutingModule + PlantnetPageRoutingModule, + TranslateModule, + PreviewPageModule + ], - declarations: [PlantnetPage] + declarations: [PlantnetPage], + }) export class PlantnetPageModule {} diff --git a/src/app/mobisplugins/plantnet/plantnet.page.html b/src/app/mobisplugins/plantnet/plantnet.page.html index 364ca24..b3a64b7 100644 --- a/src/app/mobisplugins/plantnet/plantnet.page.html +++ b/src/app/mobisplugins/plantnet/plantnet.page.html @@ -1,15 +1,32 @@ - - - New PlantNet observation - + + - - - + + +Take a picture + - + + +Welcome to the Plantnet API + - +
+ +
+ +
+ + Lets classify this image! + +
+ + + + + Take a picture + +
\ No newline at end of file diff --git a/src/app/mobisplugins/plantnet/plantnet.page.spec.ts b/src/app/mobisplugins/plantnet/plantnet.page.spec.ts index a34d411..af7c1a4 100644 --- a/src/app/mobisplugins/plantnet/plantnet.page.spec.ts +++ b/src/app/mobisplugins/plantnet/plantnet.page.spec.ts @@ -1,19 +1,19 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; -import { PlantnetPage } from './plantnet.page'; +import { IspexPage } from './plantnet.page'; describe('PlantnetPage', () => { - let component: PlantnetPage; - let fixture: ComponentFixture; + let component: IspexPage; + let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ PlantnetPage ], + declarations: [ IspexPage ], imports: [IonicModule.forRoot()] }).compileComponents(); - fixture = TestBed.createComponent(PlantnetPage); + fixture = TestBed.createComponent(IspexPage); component = fixture.componentInstance; fixture.detectChanges(); })); diff --git a/src/app/mobisplugins/plantnet/plantnet.page.ts b/src/app/mobisplugins/plantnet/plantnet.page.ts index 0c417e7..d9a3762 100644 --- a/src/app/mobisplugins/plantnet/plantnet.page.ts +++ b/src/app/mobisplugins/plantnet/plantnet.page.ts @@ -1,6 +1,20 @@ -import { Component, OnInit } from '@angular/core'; -import { HttpClient} from '@angular/common/http'; +/* eslint-disable @typescript-eslint/naming-convention */ +import * as Parse from 'parse'; +import { ENV } from '../../app.constant'; +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { ModalController } from '@ionic/angular'; +import { PreviewPage } from './preview/preview.page'; +import { TranslateService } from '@ngx-translate/core'; +import { AuthService } from '../../services/auth.service'; +import { Auth, user } from '@angular/fire/auth'; +import { Storage } from '@ionic/storage-angular'; +import { Router } from '@angular/router'; +import { Geolocation } from '@capacitor/geolocation'; +Parse.initialize(ENV.parseAppId, ENV.parseJSKey); +(Parse as any).serverURL = ENV.parseServerUrl; // use your server url + +let uploadSuccess = false; @Component({ selector: 'app-plantnet', @@ -8,24 +22,104 @@ import { HttpClient} from '@angular/common/http'; styleUrls: ['./plantnet.page.scss'], }) export class PlantnetPage implements OnInit { + uploadSuccess = false; // Add this line + image = ''; + user = null; + language = ''; + latitude: number; + longitude: number; + altitude: number; - constructor(private http: HttpClient) { } + constructor( + private modal: ModalController, + private translateService: TranslateService, + private authService: AuthService, + private afAuth: Auth, + private storage: Storage, + private router: Router + ) { + user(this.afAuth).subscribe((response) => { + //fill the user to verify if someone is logged in + this.user = response; + console.log(this.user.uid); + }); + } ngOnInit() { -// now -const data = { - PROJECT: 'all', - API_URL : 'https://my-api.plantnet.org/v2/identify/all?api-key=', - API_KEY : 'my-api-key', - API_SIMSEARCH_OPTION : '&include-related-images=true', - API_LANG : '&lang=en' - }; - - this.http.post('http://your-api-endpoint.com', data).subscribe((response) => { - console.log(response); - }); + this.getLocation(); -} + + + } + + async getLocation() { + const position = await Geolocation.getCurrentPosition({ + enableHighAccuracy: true, + }); + this.latitude = position.coords.latitude; + console.log(position.coords.latitude); + this.longitude = position.coords.longitude; + this.altitude = position.coords.altitude; + return position.coords; + } + async openCamera() { + const modal = await this.modal.create({ + component: PreviewPage, + cssClass: 'fullscreen', + animated: true, + }); + + modal.onDidDismiss().then((data) => { + if (data !== null) { + this.image = data.data; + + this.uploadSuccess = true; + // create parse class + + const plantnet_data_store = Parse.Object.extend('plantnet_data'); + + // create new instance of parse class + + const plantnet_data = new plantnet_data_store(); + + // set value for parse clas + + const file = new Parse.File('image.jpg', { base64: this.image }); + file.save().then( + (file) => { + console.log(file); + }, + (error) => { + console.log(error); + } + ); + // add location and clasiification + + plantnet_data.set('user_uid', this.user.uid); + plantnet_data.set('name', this.user.displayName); + plantnet_data.set('email', this.user.email); + plantnet_data.set('image', file); + plantnet_data.set('latitude', this.latitude); + plantnet_data.set('longitude', this.longitude); + plantnet_data.set('altitude', this.altitude); + + plantnet_data.save().then( + (result: any) => { + console.log(result); + }, + (error: any) => { + console.log(error); + } + ); + } else { + console.log('no data'); + } + }); + + return await modal.present(); + } + + // navigate to the classify page } diff --git a/src/app/mobisplugins/plantnet/preview/preview-routing.module.ts b/src/app/mobisplugins/plantnet/preview/preview-routing.module.ts new file mode 100644 index 0000000..1e1bd1d --- /dev/null +++ b/src/app/mobisplugins/plantnet/preview/preview-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { PreviewPage } from './preview.page'; + +const routes: Routes = [ + { + path: '', + component: PreviewPage + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class PreviewPageRoutingModule {} diff --git a/src/app/mobisplugins/plantnet/preview/preview.module.ts b/src/app/mobisplugins/plantnet/preview/preview.module.ts new file mode 100644 index 0000000..9f23149 --- /dev/null +++ b/src/app/mobisplugins/plantnet/preview/preview.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { PreviewPageRoutingModule } from './preview-routing.module'; + +import { PreviewPage } from './preview.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + PreviewPageRoutingModule + ], + declarations: [PreviewPage] +}) +export class PreviewPageModule {} diff --git a/src/app/mobisplugins/plantnet/preview/preview.page.html b/src/app/mobisplugins/plantnet/preview/preview.page.html new file mode 100644 index 0000000..da92b16 --- /dev/null +++ b/src/app/mobisplugins/plantnet/preview/preview.page.html @@ -0,0 +1,13 @@ + +
+ + + + + + + + + +
+
diff --git a/src/app/mobisplugins/plantnet/preview/preview.page.scss b/src/app/mobisplugins/plantnet/preview/preview.page.scss new file mode 100644 index 0000000..c883faf --- /dev/null +++ b/src/app/mobisplugins/plantnet/preview/preview.page.scss @@ -0,0 +1,42 @@ +ion-content { + --background: transparent !important; +} + +#capture { + position: absolute; + bottom: 30px; + left: calc(50% - 25px); + width: 50px; + height: 50px; + z-index: 99999; +} + +#flip { + position: absolute; + bottom: 30px; + left: calc(50% + 125px); + width: 50px; + height: 50px; + z-index: 99999; +} + +#close { + position: absolute; + bottom: 30px; + left: calc(50% - 175px); + width: 50px; + height: 50px; + z-index: 99999; +} + +#capture::part(native) { + border-radius: 30px; +} + +#close::part(native) { + border-radius: 30px; +} + +#flip::part(native) { + border-radius: 30px; +} diff --git a/src/app/mobisplugins/plantnet/preview/preview.page.spec.ts b/src/app/mobisplugins/plantnet/preview/preview.page.spec.ts new file mode 100644 index 0000000..c13b197 --- /dev/null +++ b/src/app/mobisplugins/plantnet/preview/preview.page.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { PreviewPage } from './preview.page'; + +describe('PreviewPage', () => { + let component: PreviewPage; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ PreviewPage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(PreviewPage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/mobisplugins/plantnet/preview/preview.page.ts b/src/app/mobisplugins/plantnet/preview/preview.page.ts new file mode 100644 index 0000000..8eda050 --- /dev/null +++ b/src/app/mobisplugins/plantnet/preview/preview.page.ts @@ -0,0 +1,56 @@ +import { Component, OnInit } from '@angular/core'; +import { Plugins } from "@capacitor/core" +const { CameraPreview } = Plugins; +import { CameraPreviewOptions, CameraPreviewPictureOptions } from '@capacitor-community/camera-preview'; +import '@capacitor-community/camera-preview'; +import { ModalController } from '@ionic/angular'; + +@Component({ + selector: 'app-preview', + templateUrl: './preview.page.html', + styleUrls: ['./preview.page.scss'], +}) +export class PreviewPage implements OnInit { + image =""; + cameraActive = false; + constructor(private modal: ModalController) { } + + ngOnInit() { + this.launchCamera(); + } + + launchCamera() { + const cameraPreviewOptions: CameraPreviewOptions = { + position: 'front', // front or rear + parent: 'content', // the id on the ion-content + className: '', + width: window.screen.width, //width of the camera display + height: window.screen.height - 200, //height of the camera + toBack: false, + disableAudio: true, + enableHighResolution: true, + }; + CameraPreview['start'](cameraPreviewOptions); + this.cameraActive = true; + } + + async takePicture() { + const cameraPreviewPictureOptions: CameraPreviewPictureOptions = { + quality: 90 + }; + const result = await CameraPreview['capture'](cameraPreviewPictureOptions); + this.image = `data:image/jpeg;base64,${result.value}`; + this.stopCamera(); + } + + async stopCamera() { + await CameraPreview['stop'](); + this.modal.dismiss(this.image); + } + + async flipCamera() { + await CameraPreview['flip'](); + } + + +} diff --git a/src/app/options/options.page.html b/src/app/options/options.page.html index 276b754..cd48470 100644 --- a/src/app/options/options.page.html +++ b/src/app/options/options.page.html @@ -1,9 +1,9 @@ - + - + - {{ "optionsPage.title" | translate }} + {{ "optionsPage.title" | translate }} @@ -19,8 +19,8 @@ - - {{ "optionsPage.general-settings-label" | translate }} + + {{ "optionsPage.general-settings-label" | translate }} {{ "optionsPage.language-label" | translate }} English diff --git a/src/app/options/options.page.scss b/src/app/options/options.page.scss index e69de29..d18dcae 100644 --- a/src/app/options/options.page.scss +++ b/src/app/options/options.page.scss @@ -0,0 +1,11 @@ +ion-item { + font-size: small; + flex: none; +} + +ion-select{ + max-width: 70% !important; + width: 60% !important; + padding-left: 20px !important; + text-align: end; +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 72ed9db..5d2c394 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -63,13 +63,10 @@ export class AuthService { async sha256(message) { // encode as UTF-8 const msgBuffer = new TextEncoder().encode(message); - // hash the message const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); - // convert ArrayBuffer to Array const hashArray = Array.from(new Uint8Array(hashBuffer)); - // convert bytes to hex string const hashHex = hashArray .map((b) => b.toString(16).padStart(2, '0')) @@ -84,7 +81,7 @@ export class AuthService { this.platform.ready().then(() => { GoogleAuth.initialize({ clientId: - '553589883639-2h19rvk5ki52j7h0ptjmmlh5keetm3kj.apps.googleusercontent.com', //Test new project MOBIS AUTH WEB + '1090658128897-uqpp3egk2v7d0errt8crl220e6tltblq.apps.googleusercontent.com', scopes: ['profile', 'email'], grantOfflineAccess: false, }); @@ -103,7 +100,6 @@ export class AuthService { ); await this.sendVerificationMail(); await this.updateUserName(name); - // this.updateUserName(name); return loggedInUser; } catch (e) { return null; @@ -128,6 +124,11 @@ export class AuthService { await updateProfile(this.afAuth.currentUser, { displayName: name }); } + async updatePhotoURL(photoURL: string) { + await updateProfile(this.afAuth.currentUser, { photoURL }); + } + + async sendPasswordResetEmail(email) { console.log('resetting password for email', email); try { @@ -212,9 +213,7 @@ export class AuthService { console.log('Apple Native login'); let loggedInUser = null; const nonce = this.nonceService.generateNonce(); - const hashedNonceHex = await this.sha256(nonce); // see next function - const options: SignInWithAppleOptions = { clientId: 'nl.ddq.blackholefinder', redirectURI: 'https://www.ddq.nl', @@ -222,11 +221,9 @@ export class AuthService { state: '123456', nonce: hashedNonceHex, }; - const appleUser: SignInWithAppleResponse = await SignInWithApple.authorize(options); const provider = new OAuthProvider('apple.com'); const credential = provider.credential({idToken: appleUser.response.identityToken, rawNonce: nonce}); - await signInWithCredential(this.afAuth, credential).then( (signedInUser) => { loggedInUser = signedInUser; @@ -238,4 +235,8 @@ export class AuthService { logout() { return signOut(this.afAuth); } + + deleteAccount(){ + return this.afAuth.currentUser.delete(); + } } diff --git a/src/app/services/avatar.service.ts b/src/app/services/avatar.service.ts index 4d16936..23d8715 100644 --- a/src/app/services/avatar.service.ts +++ b/src/app/services/avatar.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; -import { Auth } from '@angular/fire/auth'; -import { doc, docData, Firestore, setDoc } from '@angular/fire/firestore'; -import { ref, uploadString, getDownloadURL, Storage } from '@angular/fire/storage'; +import { Auth, updateProfile } from '@angular/fire/auth'; +import { deleteField, doc, docData, Firestore, setDoc, updateDoc } from '@angular/fire/firestore'; +import { ref, uploadString, getDownloadURL, Storage, deleteObject } from '@angular/fire/storage'; import { Photo } from '@capacitor/camera'; @Injectable({ @@ -9,17 +9,33 @@ import { Photo } from '@capacitor/camera'; }) export class AvatarService { - constructor(private auth: Auth, private firestore: Firestore, private storage: Storage) { } + constructor(private afAuth: Auth, private firestore: Firestore, private storage: Storage) { } - getUserProfile() { - const user = this.auth.currentUser; + getAvatar() { + const user = this.afAuth.currentUser; const userDocRef = doc(this.firestore, `users/${user.uid}`); const result = docData(userDocRef, { idField: 'id' }); return result; } + + async updatePhotoURL(photoURL: string) { + await updateProfile(this.afAuth.currentUser, { photoURL }); + } + + removeImage(){ + const user = this.afAuth.currentUser; + //delete the reference to the image in Firestore Database + const userDocRef = doc(this.firestore, `users/${user.uid}`); + updateDoc(userDocRef, { imageUrl: deleteField() }); + //delete the actual image in Storage + const path = `uploads/${user.uid}/profile.png`; + const storageRef = ref(this.storage, path); + deleteObject(storageRef); + } + async uploadImage(cameraFile: Photo) { - const user = this.auth.currentUser; + const user = this.afAuth.currentUser; //console.log(user); const path = `uploads/${user.uid}/profile.png`; const storageRef = ref(this.storage, path); @@ -31,8 +47,10 @@ export class AvatarService { const userDocRef = doc(this.firestore, `users/${user.uid}`); await setDoc(userDocRef, { - imageUrl, + imageUrl }); + console.log(imageUrl); + await this.updatePhotoURL(imageUrl); return true; } catch (e) { return null; diff --git a/src/app/services/weather.service.ts b/src/app/services/weather.service.ts new file mode 100644 index 0000000..74cbdb3 --- /dev/null +++ b/src/app/services/weather.service.ts @@ -0,0 +1,115 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +export interface WeatherData{ + temp: number; + description: string; + iconUrl: string; +} + +@Injectable({ + providedIn: 'root' +}) + +export class WeatherService { + public blackGemCoords: {lat: number; lng: number} = {lat: -29.257674, lng: -70.737855}; + public meerLichtCoords: {lat: number; lng: number} = {lat: -32.379864, lng: 20.811234}; + + userLat: number; + userLng: number; + + constructor(private http: HttpClient) { } + + //function that gets the users current location and then calls the showWeather function + showCurrentWeather() { + this.getLocation().then((pos: {lat: number; lng: number}) => { + console.log(`Positon: ${pos.lat} ${pos.lng}`); + this.userLat = pos.lat; + this.userLng = pos.lng; + this.showWeather(pos.lat, pos.lng); + }); + } + + //function that returns the users current location as a promise from the geolocation API + getLocation() { + return new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + resp => { + resolve({ lat: resp.coords.latitude, lng: resp.coords.longitude }); + }, + err => { + reject(err); + } + ); + }); + } + + //function that writes all weather info from the result of the openweathermap.org API in a nice pop up window + showWeather(lat: number, lng: number) { + this.http + .get( + 'https://api.openweathermap.org/data/2.5/weather?lat=' + + lat + + '&lon=' + + lng + + '&appid=edd7d6eab104383027cd6cc21f32d772&units=metric' + ) + .subscribe((res: any) => { + const temp = res.main.temp; + const iconcode = res.weather[0].icon; + console.log(iconcode); + const iconurl = 'http://openweathermap.org/img/w/' + iconcode + '.png'; + console.log(temp); + console.log(iconurl); + //display the weather icon, fetches the temperature from the weather.main object + const weatherPopup = window.open( + '', + 'Weather', + 'width=300,height=300' + ); + weatherPopup.document.write( + '

Current weather: ' + + res.weather[0].description + + '

Temperature: ' + + temp + + '°C

' + ); + }); + } + + //function that returns the weather from openweathermap.org API and returns a single line of text with all weather info as an observable + getWeather(lat: number, lng: number): Promise { + let description: string; + let temp: number; + let iconUrl: string; + return new Promise((resolve, reject) => { + try { + this.http + .get( + 'https://api.openweathermap.org/data/2.5/weather?lat=' + + lat + + '&lon=' + + lng + + '&appid=edd7d6eab104383027cd6cc21f32d772&units=metric' + ).subscribe((res: any) => { + temp = res.main.temp; + const iconcode = res.weather[0].icon; + description = res.weather[0].description; + //console.log(iconcode); + iconUrl = 'http://openweathermap.org/img/w/' + iconcode + '.png'; + console.log(temp); + console.log(iconUrl); + console.log(description); + resolve({temp, description, iconUrl}); + }); + + } + catch (error) { + reject(error); + } + }); + } + +}