Initial commit
@@ -0,0 +1,70 @@
|
||||
# Specifies intentionally untracked files to ignore when using Git
|
||||
# http://git-scm.com/docs/gitignore
|
||||
|
||||
*~
|
||||
*.sw[mnpcod]
|
||||
.tmp
|
||||
*.tmp
|
||||
*.tmp.*
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
UserInterfaceState.xcuserstate
|
||||
$RECYCLE.BIN/
|
||||
|
||||
*.log
|
||||
log.txt
|
||||
npm-debug.log*
|
||||
|
||||
/.angular
|
||||
/.idea
|
||||
/.ionic
|
||||
/.sass-cache
|
||||
/.sourcemaps
|
||||
/.versions
|
||||
/.vscode
|
||||
/coverage
|
||||
/dist
|
||||
/node_modules
|
||||
/platforms
|
||||
/plugins
|
||||
/www
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
*-debug.log
|
||||
.runtimeconfig.json
|
||||
src/app/app.constant.ts
|
||||
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
/src/app/firebase.json
|
||||
/src/environments/environment.prod.ts
|
||||
/src/environments/environment.ts
|
||||
/src/app/app.constant.ts
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
@@ -1,4 +1,4 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
# MOBIS_COS4CLOUD
|
||||

|
||||
|
||||
|
||||
# MOBIS FRONT END SUPPORT PACKAGE
|
||||
|
||||
|
||||
# What is MOBIS?
|
||||
|
||||
|
||||
## Introduction
|
||||
Mobis is a citizen science platform that enables app developers and researchers to incorporate air and water quality monitoring, as well as biodiversity research, into their own apps. The software includes example integrations with Canair.io, mini-secchi, iSPEX and Plant*Net, which are tools for measuring air and water quality and studying biodiversity, respectively.
|
||||
Mobis also includes features for user login and storage on the European Open Science Cloud. This makes it easy for researchers and app developers to collect and share data for their projects.
|
||||
|
||||
|
||||
# About this repository
|
||||
|
||||
Here you will find software and documentation for our low cost sensors (iSPEX, kDUINO, Fresh Water Watch)
|
||||
The software is developed using the IONIC framework, Parse Server (Back End).
|
||||
For iSPEX(r) we offer a basic interface. The iSPEX (r) professional software is using native closed source software and developed outside the EU research project.
|
||||
|
||||
|
||||
Mobis is an open source citizen science software framework that allows app developers and researchers to easily integrate air and water quality monitoring, as well as biodiversity research, into their own apps.
|
||||
The sample software includes integrations with Canair.io (for air quality), mini-secchi (for water quality), and Plant*Net (for biodiversity research), as well as user login and storage on the European Open Science Cloud.
|
||||
|
||||
This readme acts as a reference for the mobis software and / or documentation. Both software and documentation are still into development, so the contents of this file/wiki can and will change over time.
|
||||
|
||||
|
||||
## Audience
|
||||
Scientists, (app) developers, project partners, people who are lookikng for low cost sensor integration solutions.
|
||||
|
||||
|
||||
## Generic
|
||||
Provided functionalities in R 1.0:
|
||||
User login (Apple/Google/Email)
|
||||
Language switcher
|
||||
Data sync/upload
|
||||
|
||||
WIP:
|
||||
Full offline support (Parse Server)
|
||||
Push notifications
|
||||
|
||||
# Plugin description
|
||||
### General description
|
||||
|
||||
## CANAIR.IO
|
||||
### Description
|
||||
|
||||
## MINI SECCHI
|
||||
### Description of the mobis Mini Secchi Plugin
|
||||
Mini Secchi is a water quality tool that measures the Secchi depth of a lake or river. This is a measure of the water transparency and is used to assess water quality. The Mini Secchi is a low-cost, open-source sensor that can be used to collect water quality data in lakes, rivers and streams.
|
||||
|
||||
## iSPEX
|
||||
### Description of the mobis iSPEX spectropolarimeter Plugin
|
||||
|
||||
iSPEX is a spectropolarimeter that measures the optical properties of water or air. It is a low-cost, open-source sensor that can be used to collect water quality data in lakes, rivers and streams.
|
||||
|
||||
|
||||
## Description of the mobis PLANT*NET API plugin
|
||||
### Description
|
||||
Plant*Net is a platformfor plant identification
|
||||
that allows users to identify plants using a smartphone camera.
|
||||
|
||||
# BACK END SCRIPTS
|
||||
|
||||
Mobile back end. Used for storage, metadata, push notifications, offline sync.
|
||||
Parse Server
|
||||
|
||||
### Parse Server setup
|
||||
You can run your own (Dockerized) Parse Server or use the Cos4Cloud instance.
|
||||
If you want to run your own instance, please check the [Parse Server documentation](https://docs.parseplatform.org/parse-server/guide/).
|
||||
If you want to use the Cos4Cloud instance, please contact us via the [Cos4Cloud Slack channel](https://join.slack.com/t/cos4cloud/shared_invite/zt-8q3vzq1p-7b3hGdYk8jvRyRJd1Nn9gg).
|
||||
|
||||
|
||||
### Sensor Things API 1.1 support
|
||||
The Cos4Cloud instance of Parse Server supports the Sensor Things API 1.1.
|
||||
Please check the [Sensor Things API 1.1 documentation](https://developers.sensorup.com/docs/#section/Introduction) for more information.
|
||||
|
||||
# FUNDING
|
||||
|
||||

|
||||
|
||||
This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement No 863463
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"app": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "www",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*.svg",
|
||||
"input": "node_modules/ionicons/dist/ionicons/svg",
|
||||
"output": "./svg"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/theme/variables.scss",
|
||||
"src/global.scss",
|
||||
"src/styles.css",
|
||||
"./node_modules/leaflet/dist/leaflet.css",
|
||||
"node_modules/leaflet.markercluster/dist/MarkerCluster.css",
|
||||
"node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"buildOptimizer": false,
|
||||
"sourceMap": true,
|
||||
"optimization": false,
|
||||
"namedChunks": true
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "app:build:production"
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"styles": [],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
{
|
||||
"glob": "favicon.ico",
|
||||
"input": "src/",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "/assets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"progress": false,
|
||||
"watch": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "app:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "app:serve:production"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "app:serve:ci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deploy": {
|
||||
"builder": "@angular/fire:deploy",
|
||||
"options": {
|
||||
"prerender": false,
|
||||
"ssr": false,
|
||||
"browserTarget": "app:build:production",
|
||||
"firebaseProject": "mobis-auth",
|
||||
"firebaseHostingSite": "mobis-auth"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"schematicCollections": [
|
||||
"@ionic/angular-toolkit"
|
||||
],
|
||||
"analytics": false
|
||||
},
|
||||
"schematics": {
|
||||
"@ionic/angular-toolkit:component": {
|
||||
"styleext": "scss"
|
||||
},
|
||||
"@ionic/angular-toolkit:page": {
|
||||
"styleext": "scss"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"appId": "nl.ddq.mobisapp",
|
||||
"appName": "mobisapp",
|
||||
"bundledWebRuntime": false,
|
||||
"npmClient": "npm",
|
||||
"webDir": "dist/demo",
|
||||
"cordova": {},
|
||||
"plugins": {
|
||||
"GoogleAuth": {
|
||||
"scopes": ["profile", "email"],
|
||||
"clientId": "778522712103-tqkc468qermr7hvbqsmm2p2ueglcmjsi.apps.googleusercontent.com",
|
||||
"forceCodeForRefreshToken" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { CapacitorConfig } from '@capacitor/cli';
|
||||
|
||||
const config: CapacitorConfig = {
|
||||
appId: 'nl.ddq.mobisapp',
|
||||
appName: 'mobisapp',
|
||||
webDir: 'www',
|
||||
bundledWebRuntime: false,
|
||||
plugins: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
GoogleAuth: {
|
||||
scopes: ['profile', 'email'],
|
||||
androidClientId: '778522712103-tqkc468qermr7hvbqsmm2p2ueglcmjsi.apps.googleusercontent.com',
|
||||
forceCodeForRefreshToken: true,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,101 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="nl.ddq.mobisapp" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>mobisapp</name>
|
||||
<description>The Mobis demonstration app.</description>
|
||||
<author email="web@ddq.nl" href="http://ionicframework.com/">DDQ/Pocket science</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<preference name="ScrollEnabled" value="false" />
|
||||
<preference name="BackupWebStorage" value="none" />
|
||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
||||
<preference name="FadeSplashScreenDuration" value="300" />
|
||||
<preference name="SplashShowOnlyFirstTime" value="false" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<preference name="SplashScreenDelay" value="3000" />
|
||||
<platform name="android">
|
||||
<preference name="Scheme" value="http" />
|
||||
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:networkSecurityConfig="@xml/network_security_config" />
|
||||
</edit-config>
|
||||
<resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
|
||||
<allow-intent href="market:*" />
|
||||
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
|
||||
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
|
||||
<icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
|
||||
<icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
|
||||
<icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
|
||||
<icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
|
||||
<splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
|
||||
<splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
|
||||
<splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
|
||||
<splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
|
||||
<splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
|
||||
<splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
|
||||
<splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
|
||||
<splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
|
||||
<splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
|
||||
<splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
|
||||
<splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
|
||||
<splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
<icon height="57" src="resources/ios/icon/icon.png" width="57" />
|
||||
<icon height="114" src="resources/ios/icon/icon@2x.png" width="114" />
|
||||
<icon height="29" src="resources/ios/icon/icon-small.png" width="29" />
|
||||
<icon height="58" src="resources/ios/icon/icon-small@2x.png" width="58" />
|
||||
<icon height="87" src="resources/ios/icon/icon-small@3x.png" width="87" />
|
||||
<icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
|
||||
<icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
|
||||
<icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
|
||||
<icon height="48" src="resources/ios/icon/icon-24@2x.png" width="48" />
|
||||
<icon height="55" src="resources/ios/icon/icon-27.5@2x.png" width="55" />
|
||||
<icon height="29" src="resources/ios/icon/icon-29.png" width="29" />
|
||||
<icon height="58" src="resources/ios/icon/icon-29@2x.png" width="58" />
|
||||
<icon height="87" src="resources/ios/icon/icon-29@3x.png" width="87" />
|
||||
<icon height="40" src="resources/ios/icon/icon-40.png" width="40" />
|
||||
<icon height="80" src="resources/ios/icon/icon-40@2x.png" width="80" />
|
||||
<icon height="120" src="resources/ios/icon/icon-40@3x.png" width="120" />
|
||||
<icon height="88" src="resources/ios/icon/icon-44@2x.png" width="88" />
|
||||
<icon height="50" src="resources/ios/icon/icon-50.png" width="50" />
|
||||
<icon height="100" src="resources/ios/icon/icon-50@2x.png" width="100" />
|
||||
<icon height="60" src="resources/ios/icon/icon-60.png" width="60" />
|
||||
<icon height="120" src="resources/ios/icon/icon-60@2x.png" width="120" />
|
||||
<icon height="180" src="resources/ios/icon/icon-60@3x.png" width="180" />
|
||||
<icon height="72" src="resources/ios/icon/icon-72.png" width="72" />
|
||||
<icon height="144" src="resources/ios/icon/icon-72@2x.png" width="144" />
|
||||
<icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
|
||||
<icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
|
||||
<icon height="167" src="resources/ios/icon/icon-83.5@2x.png" width="167" />
|
||||
<icon height="172" src="resources/ios/icon/icon-86@2x.png" width="172" />
|
||||
<icon height="196" src="resources/ios/icon/icon-98@2x.png" width="196" />
|
||||
<icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
|
||||
<splash height="480" src="resources/ios/splash/Default~iphone.png" width="320" />
|
||||
<splash height="960" src="resources/ios/splash/Default@2x~iphone.png" width="640" />
|
||||
<splash height="1024" src="resources/ios/splash/Default-Portrait~ipad.png" width="768" />
|
||||
<splash height="768" src="resources/ios/splash/Default-Landscape~ipad.png" width="1024" />
|
||||
<splash height="1125" src="resources/ios/splash/Default-Landscape-2436h.png" width="2436" />
|
||||
<splash height="1242" src="resources/ios/splash/Default-Landscape-736h.png" width="2208" />
|
||||
<splash height="2048" src="resources/ios/splash/Default-Portrait@2x~ipad.png" width="1536" />
|
||||
<splash height="1536" src="resources/ios/splash/Default-Landscape@2x~ipad.png" width="2048" />
|
||||
<splash height="2732" src="resources/ios/splash/Default-Portrait@~ipadpro.png" width="2048" />
|
||||
<splash height="2048" src="resources/ios/splash/Default-Landscape@~ipadpro.png" width="2732" />
|
||||
<splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
|
||||
<splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />
|
||||
<splash height="2208" src="resources/ios/splash/Default-736h.png" width="1242" />
|
||||
<splash height="2436" src="resources/ios/splash/Default-2436h.png" width="1125" />
|
||||
<splash height="2732" src="resources/ios/splash/Default@2x~universal~anyany.png" width="2732" />
|
||||
</platform>
|
||||
<plugin name="cordova-plugin-statusbar" spec="2.4.2" />
|
||||
<plugin name="cordova-plugin-device" spec="2.0.2" />
|
||||
<plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
|
||||
<plugin name="cordova-plugin-ionic-webview" spec="^5.0.0" />
|
||||
<plugin name="cordova-plugin-ionic-keyboard" spec="^2.0.5" />
|
||||
</widget>
|
||||
@@ -0,0 +1,37 @@
|
||||
// @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
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.deepCss('app-root ion-content')).getText();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "mobisapp",
|
||||
"integrations": {
|
||||
"capacitor": {},
|
||||
"cordova": {}
|
||||
},
|
||||
"type": "angular"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/ngv'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"name": "firebase-login",
|
||||
"version": "0.0.1",
|
||||
"author": "Ionic Framework",
|
||||
"homepage": "https://ionicframework.com/",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^14.0.0",
|
||||
"@angular/core": "^14.0.0",
|
||||
"@angular/fire": "^7.4.1",
|
||||
"@angular/forms": "^14.0.0",
|
||||
"@angular/platform-browser": "^14.0.0",
|
||||
"@angular/platform-browser-dynamic": "^14.0.0",
|
||||
"@angular/router": "^14.0.0",
|
||||
"@awesome-cordova-plugins/background-mode": "^6.2.0",
|
||||
"@awesome-cordova-plugins/core": "6.2.0",
|
||||
"@capacitor-community/apple-sign-in": "^4.0.0",
|
||||
"@capacitor-community/bluetooth-le": "^2.0.1",
|
||||
"@capacitor-community/camera-preview": "^4.0.0",
|
||||
"@capacitor/android": "4.0.1",
|
||||
"@capacitor/app": "4.0.1",
|
||||
"@capacitor/camera": "^4.0.1",
|
||||
"@capacitor/core": "4.0.1",
|
||||
"@capacitor/device": "4.1.0",
|
||||
"@capacitor/geolocation": "^4.0.1",
|
||||
"@capacitor/haptics": "4.0.1",
|
||||
"@capacitor/ios": "4.0.1",
|
||||
"@capacitor/keyboard": "4.0.1",
|
||||
"@capacitor/status-bar": "4.0.1",
|
||||
"@codetrix-studio/capacitor-google-auth": "^3.2.0",
|
||||
"@ionic-native/vibration": "^5.36.0",
|
||||
"@ionic/angular": "^6.1.9",
|
||||
"@ionic/pwa-elements": "^3.1.1",
|
||||
"@ionic/storage-angular": "^3.0.6",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
"@ngx-translate/http-loader": "^7.0.0",
|
||||
"@types/leaflet.markercluster": "^1.5.1",
|
||||
"@types/parse": "^2.18.18",
|
||||
"cordova-plugin-background-mode": "^0.7.3",
|
||||
"cordova-plugin-device": "2.1.0",
|
||||
"guid-typescript": "^1.0.9",
|
||||
"leaflet": "^1.9.2",
|
||||
"leaflet.markercluster": "^1.5.3",
|
||||
"ng-apexcharts": "^1.7.1",
|
||||
"parse": "^3.5.1",
|
||||
"rxjs": "^7.5.7",
|
||||
"stream": "^0.0.2",
|
||||
"timers": "^0.1.1",
|
||||
"tslib": "^2.2.0",
|
||||
"xml2js": "^0.4.23",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^14.0.0",
|
||||
"@angular-eslint/builder": "~13.0.1",
|
||||
"@angular-eslint/eslint-plugin": "~13.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "~13.0.1",
|
||||
"@angular-eslint/template-parser": "~13.0.1",
|
||||
"@angular/cli": "^14.0.0",
|
||||
"@angular/compiler": "^14.0.0",
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
"@angular/language-service": "^14.0.0",
|
||||
"@capacitor/cli": "4.0.1",
|
||||
"@ionic/angular-toolkit": "^6.0.0",
|
||||
"@ionic/cli": "6.20.4",
|
||||
"@ionic/lab": "3.2.15",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
"@typescript-eslint/eslint-plugin": "5.3.0",
|
||||
"@typescript-eslint/parser": "5.3.0",
|
||||
"cordova-res": "0.15.4",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsdoc": "30.7.6",
|
||||
"eslint-plugin-prefer-arrow": "1.2.2",
|
||||
"jasmine-core": "~3.8.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.3.2",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.0.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"typescript": "~4.7.3"
|
||||
},
|
||||
"description": "An Ionic project"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
## Capacitor Plugins
|
||||
|
||||
### @capacitor-community
|
||||
- **apple-sign-in@4.0.0**
|
||||
### @capacitor
|
||||
- **app@4.0.1** - (Latest 4.1.1)
|
||||
- **camera@4.1.3** - (Latest 4.1.4)
|
||||
- **geolocation@4.0.1** - (Latest 4.1.0)
|
||||
- **haptics@4.0.1** - (Latest 4.1.0)
|
||||
- **keyboard@4.0.1** - (Latest 4.1.0)
|
||||
- **status-bar@4.0.1** - (Latest 4.1.1)
|
||||
### @codetrix-studio
|
||||
- **capacitor-google-auth@3.2.0**
|
||||
## Cordova Plugins
|
||||
|
||||
## Dependencies
|
||||
|
||||
### @angular-devkit
|
||||
- **build-angular@14.2.6** - (Latest 15.0.2)
|
||||
### @angular-eslint
|
||||
- **builder@13.0.1** - (Latest 15.1.0)
|
||||
- **eslint-plugin@13.0.1** - (Latest 15.1.0)
|
||||
- **eslint-plugin-template@13.0.1** - (Latest 15.1.0)
|
||||
- **template-parser@13.0.1** - (Latest 15.1.0)
|
||||
### @angular
|
||||
- **cli@14.2.6** - (Latest 15.0.2)
|
||||
- **common@14.2.7** - (Latest 15.0.2)
|
||||
- **compiler@14.2.7** - (Latest 15.0.2)
|
||||
- **compiler-cli@14.2.7** - (Latest 15.0.2)
|
||||
- **core@14.2.7** - (Latest 15.0.2)
|
||||
- **fire@7.4.1** - (Latest 7.5.0)
|
||||
- **forms@14.2.7** - (Latest 15.0.2)
|
||||
- **language-service@14.2.7** - (Latest 15.0.2)
|
||||
- **platform-browser@14.2.7** - (Latest 15.0.2)
|
||||
- **platform-browser-dynamic@14.2.7** - (Latest 15.0.2)
|
||||
- **router@14.2.7** - (Latest 15.0.2)
|
||||
### @capacitor
|
||||
- **android@4.0.1** - (Latest 4.6.1)
|
||||
- **cli@4.0.1** - (Latest 4.6.1)
|
||||
- **core@4.0.1** - (Latest 4.6.1)
|
||||
- **ios@4.0.1** - (Latest 4.6.1)
|
||||
### @ionic
|
||||
- **angular@6.3.2** - (Latest 6.3.9)
|
||||
- **angular-toolkit@6.1.0** - (Latest 7.0.0)
|
||||
- **cli@6.20.4**
|
||||
- **pwa-elements@3.1.1**
|
||||
- **storage-angular@3.0.6**
|
||||
### @ngx-translate
|
||||
- **core@14.0.0**
|
||||
- **http-loader@7.0.0**
|
||||
### @types
|
||||
- **jasmine@3.6.11** - (Latest 4.3.1)
|
||||
- **jasminewd2@2.0.10**
|
||||
- **leaflet@1.9.0**
|
||||
- **leaflet.markercluster@1.5.1**
|
||||
- **node@12.20.55** - (Latest 18.11.11)
|
||||
- **parse@2.18.18** - (Latest 3.0.1)
|
||||
- **xml2js@0.4.11**
|
||||
### @typescript-eslint
|
||||
- **eslint-plugin@5.3.0** - (Latest 5.45.1)
|
||||
- **parser@5.3.0** - (Latest 5.45.1)
|
||||
### Other Dependencies
|
||||
- **cordova-res@0.15.4**
|
||||
- **eslint@7.32.0** - (Latest 8.29.0)
|
||||
- **eslint-plugin-import@2.22.1** - (Latest 2.26.0)
|
||||
- **eslint-plugin-jsdoc@30.7.6** - (Latest 39.6.4)
|
||||
- **eslint-plugin-prefer-arrow@1.2.2** - (Latest 1.2.3)
|
||||
- **jasmine-core@3.8.0** - (Latest 4.5.0)
|
||||
- **jasmine-spec-reporter@5.0.2** - (Latest 7.0.0)
|
||||
- **karma@6.3.20** - (Latest 6.4.1)
|
||||
- **karma-chrome-launcher@3.1.1**
|
||||
- **karma-coverage@2.0.3** - (Latest 2.2.0)
|
||||
- **karma-coverage-istanbul-reporter@3.0.3**
|
||||
- **karma-jasmine@4.0.2** - (Latest 5.1.0)
|
||||
- **karma-jasmine-html-reporter@1.7.0** - (Latest 2.0.0)
|
||||
- **leaflet@1.9.2** - (Latest 1.9.3)
|
||||
- **leaflet.markercluster@1.5.3**
|
||||
- **ng-apexcharts@1.7.4**
|
||||
- **parse@3.4.4** - (Latest 3.5.1)
|
||||
- **protractor@7.0.0**
|
||||
- **rxjs@7.5.7** - (Latest 7.6.0)
|
||||
- **stream@0.0.2**
|
||||
- **timers@0.1.1**
|
||||
- **ts-node@8.3.0** - (Latest 10.9.1)
|
||||
- **tslib@2.4.0** - (Latest 2.4.1)
|
||||
- **typescript@4.7.4** - (Latest 4.9.3)
|
||||
- **xml2js@0.4.23**
|
||||
- **zone.js@0.11.8** - (Latest 0.12.0)
|
||||
|
||||
|
||||
## Nonstandard naming
|
||||
The following files and folders do not follow the standard naming convention:
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
These are Cordova resources. You can replace icon.png and splash.png and run
|
||||
`ionic cordova resources` to generate custom icons and splash screens for your
|
||||
app. See `ionic cordova resources --help` for details.
|
||||
|
||||
Cordova reference documentation:
|
||||
|
||||
- Icons: https://cordova.apache.org/docs/en/latest/config_ref/images.html
|
||||
- Splash Screens: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 97 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 616 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 978 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,88 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
||||
import {redirectUnauthorizedTo, redirectLoggedInTo, canActivate } from '@angular/fire/auth-guard';
|
||||
|
||||
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']);
|
||||
const redirectLoggedInToProfile = () => redirectLoggedInTo(['profile']);
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'home',
|
||||
loadChildren: () => import('./home/home.module').then( m => m.HomePageModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'home',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
loadChildren: () => import('./auth/login/login.module').then( m => m.LoginPageModule),
|
||||
...canActivate(redirectLoggedInToProfile)
|
||||
},
|
||||
{
|
||||
path: 'resetpw',
|
||||
loadChildren: () => import('./auth/resetpw/resetpw.module').then( m => m.ResetpwPageModule)
|
||||
},
|
||||
{
|
||||
path: 'profile',
|
||||
loadChildren: () => import('./auth/profile/profile.module').then( m => m.ProfilePageModule),
|
||||
...canActivate(redirectUnauthorizedToLogin)
|
||||
},
|
||||
{
|
||||
path: 'signup',
|
||||
loadChildren: () => import('./auth/signup/signup.module').then( m => m.SignupPageModule)
|
||||
},
|
||||
{
|
||||
path: 'observe',
|
||||
loadChildren: () => import('./observe/observe.module').then( m => m.MeasurePageModule)
|
||||
},
|
||||
{
|
||||
path: 'options',
|
||||
loadChildren: () => import('./options/options.module').then( m => m.OptionsPageModule)
|
||||
},
|
||||
{
|
||||
path: 'plantnet',
|
||||
loadChildren: () => import('./mobisplugins/plantnet/plantnet.module').then( m => m.PlantnetPageModule)
|
||||
},
|
||||
{
|
||||
path: 'ispex',
|
||||
loadChildren: () => import('./mobisplugins/ispex/ispex.module').then( m => m.IspexPageModule)
|
||||
},
|
||||
{
|
||||
path: 'minisecchi',
|
||||
loadChildren: () => import('./mobisplugins/minisecchi/minisecchi.module').then( m => m.MinisecchiPageModule)
|
||||
},
|
||||
{
|
||||
path: 'canairiopm25',
|
||||
loadChildren: () => import('./mobisplugins/canairiopm25/canairiopm25.module').then( m => m.Canairiopm25PageModule)
|
||||
},
|
||||
|
||||
{
|
||||
path: 'canairioco2',
|
||||
loadChildren: () => import('./mobisplugins/canairioco2/canairioco2.module').then( m => m.Canairioco2PageModule)
|
||||
},
|
||||
|
||||
{
|
||||
path: 'ms_measure',
|
||||
loadChildren: () => import('./mobisplugins/minisecchi/measure/measure.module').then( m => m.MeasurePageModule)
|
||||
},
|
||||
{
|
||||
path: 'ms_instructions',
|
||||
loadChildren: () => import('./mobisplugins/minisecchi/instructions/instructions.module').then( m => m.InstructionsPageModule)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
@@ -0,0 +1,60 @@
|
||||
<ion-app>
|
||||
<ion-menu side="start" contentId="main">
|
||||
<ion-menu-toggle>
|
||||
<ion-header color="primary">
|
||||
<ion-toolbar>
|
||||
|
||||
<ion-item lines="none">
|
||||
<ion-label routerLink="home">M O B I S</ion-label>
|
||||
<ion-buttons>
|
||||
<ion-button type="icon-only" (click)="closeMenu()"><ion-icon name="close-outline" color="dark"></ion-icon></ion-button>
|
||||
</ion-buttons>
|
||||
</ion-item>
|
||||
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
</ion-menu-toggle>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>{{ "menu.account" | translate }}</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-menu-toggle>
|
||||
<ion-item button routerLink="signup" [disabled]="user">
|
||||
<ion-icon name="person-add-outline"></ion-icon>
|
||||
<ion-label>{{ "menu.signup" | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
<ion-menu-toggle>
|
||||
<ion-item button routerLink="login" [disabled]="user">
|
||||
<ion-icon name="log-in-outline"></ion-icon>
|
||||
<ion-label>{{ "menu.signin" | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
|
||||
<ion-menu-toggle>
|
||||
<ion-item button routerLink="profile" [disabled]="!user">
|
||||
<ion-icon name="person-circle-outline"></ion-icon>
|
||||
<ion-label>{{ "menu.profile" | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
<ion-menu-toggle>
|
||||
<ion-item button (click)="logout()" [disabled]="!user">
|
||||
<ion-icon name="log-out-outline"></ion-icon>
|
||||
<ion-label>{{ "menu.signout" | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
<ion-list-header>
|
||||
<ion-label>{{ "menu.settings" | translate }}</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-menu-toggle>
|
||||
<ion-item button routerLink="options">
|
||||
<ion-icon name="settings-outline"></ion-icon>
|
||||
<ion-label>{{ "menu.options" | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-menu-toggle>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
<ion-router-outlet id="main"></ion-router-outlet>
|
||||
</ion-app>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AppComponent],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
// TODO: add more tests!
|
||||
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Auth, user } from '@angular/fire/auth';
|
||||
import { Router } from '@angular/router';
|
||||
import { MenuController } from '@ionic/angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { Storage } from '@ionic/storage-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit{
|
||||
user = null;
|
||||
language= '';
|
||||
|
||||
constructor(
|
||||
private menuCtrl: MenuController,
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
private afAuth: Auth,
|
||||
private translateService: TranslateService,
|
||||
private storage: Storage
|
||||
) {
|
||||
user(this.afAuth).subscribe((response) => {
|
||||
//fill the user to verify if someone is logged in
|
||||
this.user = response;
|
||||
});
|
||||
}
|
||||
async ngOnInit() {
|
||||
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();
|
||||
//if
|
||||
}
|
||||
this.translateService.use(this.language);
|
||||
}
|
||||
|
||||
|
||||
closeMenu() {
|
||||
this.menuCtrl.close();
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.authService.logout();
|
||||
this.router.navigateByUrl('/home', { replaceUrl: true });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
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 { 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';
|
||||
import { IonicStorageModule } from '@ionic/storage-angular';
|
||||
|
||||
//define loader to load local files(on given path) with HttpClient
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
export function createTranslateLoader(http: HttpClient) {
|
||||
return new TranslateHttpLoader(http, './assets/languages/', '.json');
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
IonicModule.forRoot(),
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
IonicStorageModule.forRoot(),
|
||||
TranslateModule.forRoot({loader: {provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [HttpClient]}}),
|
||||
provideFirebaseApp(() => initializeApp(environment.firebase)),
|
||||
provideAuth(() => getAuth()),
|
||||
provideFirestore(() => getFirestore()),
|
||||
provideStorage(() => getStorage()),
|
||||
],
|
||||
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { LoginPage } from './login.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: LoginPage
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class LoginPageRoutingModule {}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
|
||||
import { LoginPageRoutingModule } from './login-routing.module';
|
||||
|
||||
import { LoginPage } from './login.page';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
ReactiveFormsModule,
|
||||
LoginPageRoutingModule,
|
||||
TranslateModule
|
||||
],
|
||||
declarations: [LoginPage]
|
||||
})
|
||||
export class LoginPageModule {}
|
||||
@@ -0,0 +1,52 @@
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar color="primary">
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button defaultHref="home" icon="chevron-back-outline"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title class="ion-text-center">{{ "loginPage.title" | translate}}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-card>
|
||||
<ion-card-content>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col sizeSm="6" offsetSm="3" sizeLg="4" offsetLg="4" sizeMd="6" offsetMd="3" *ngIf="showForm" class="ion-text-center">
|
||||
<form (ngSubmit)="login()" [formGroup]="credentials">
|
||||
<ion-item fill="solid" class="ion-margin-bottom">
|
||||
<ion-input type="email" [placeholder]="'loginPage.email-placeholder' | translate" formControlName="email"><ion-icon name="mail-outline"></ion-icon></ion-input>
|
||||
<ion-icon name="person-outline" slot=“end” align-self-center></ion-icon>
|
||||
<ion-note slot="error" *ngIf="(email.dirty || email.touched) && email.errors">{{ "loginPage.email-invalid" | translate }}</ion-note>
|
||||
</ion-item>
|
||||
<ion-item fill="solid" class="ion-margin-bottom">
|
||||
<ion-input type="password" [placeholder]="'loginPage.password-placeholder' | translate" formControlName="password"><ion-icon name="key-outline"></ion-icon></ion-input>
|
||||
<ion-note slot="error" *ngIf="(password.dirty || password.touched) && password.errors">{{ "loginPage.password-invalid" | translate }}</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-button type="submit" expand="block" [disabled]="!credentials.valid">{{ "loginPage.login-button" | translate }}</ion-button>
|
||||
<a [routerLink]="['/resetpw']">{{ "loginPage.forgot-password" | translate }}</a>
|
||||
</form>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col class="ion-text-center" sizeSm="6" offsetSm="3" sizeLg="4" offsetLg="4" sizeMd="6" offsetMd="3">
|
||||
<ion-button type="button" (click)="toggleForm()" expand="block"><ion-icon name="person-outline"></ion-icon>{{ "loginPage.login-username" | translate }}</ion-button>
|
||||
<ion-button type="button" (click)="anonymousLogin()" expand="block"><ion-icon name="eye-outline"></ion-icon>{{ "loginPage.login-anonymous" | translate }}</ion-button>
|
||||
<ion-button type="button" (click)="loginWithGoogle()" expand="block" color="danger"><ion-icon name="logo-google"></ion-icon>{{ "loginPage.login-google" | translate }}</ion-button>
|
||||
<ion-button type="button" (click)="loginWithApple()" expand="block" color="dark"><ion-icon name="logo-apple"></ion-icon>{{ "loginPage.login-apple" | translate }}</ion-button>
|
||||
<a href="signup">{{ "loginPage.goto-signup" | translate }}</a>
|
||||
|
||||
<br>
|
||||
|
||||
</ion-col>
|
||||
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
|
||||
</ion-content>
|
||||
@@ -0,0 +1,11 @@
|
||||
ion-input ion-icon{
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
ion-button {
|
||||
border: 1px solid var(--ion-color-grey);
|
||||
border-radius: 16px;
|
||||
--color-hover: var(--ion-color-primary)
|
||||
}
|
||||
|
||||
|
||||