diff --git a/Makefile b/Makefile index c6a53ae..dd27065 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ install: ionic cordova plugin add cordova-plugin-local-notification npm install --save @ionic-native/local-notifications + npm install --save @ionic-native/background-mode + npm install --save @ionic-native/app-minimize npm install --save @ionic-native/core npm install --save @capacitor/core diff --git a/build/android/ic_launcher.svg b/build/android/ic_launcher.svg index d077488..b462269 100644 --- a/build/android/ic_launcher.svg +++ b/build/android/ic_launcher.svg @@ -9,9 +9,9 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg9367" version="1.1" - viewBox="0 0 75.361905 75.361907" - height="75.361908mm" - width="75.361908mm" + viewBox="0 0 74.938352 74.938354" + height="74.938354mm" + width="74.938354mm" sodipodi:docname="ic_launcher.svg" inkscape:version="1.0.2 (e86c870879, 2021-01-15)"> image/svg+xml - + - + diff --git a/build/android/ic_launcher_round.svg b/build/android/ic_launcher_round.svg deleted file mode 100644 index d6ce0e6..0000000 --- a/build/android/ic_launcher_round.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - diff --git a/config.xml b/config.xml index cfbd671..7824d23 100644 --- a/config.xml +++ b/config.xml @@ -1,5 +1,5 @@ - + METAsocket WowApp's awesome instant messenger for the whole Greifentanzgeschwader sabolli diff --git a/package-lock.json b/package-lock.json index c602cfe..b722a1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,6 @@ "@ionic-native/core": "^5.31.1", "@ionic-native/foreground-service": "^5.31.1", "@ionic-native/local-notifications": "^5.31.1", - "@ionic-native/network": "^5.31.1", - "@ionic-native/power-management": "^5.31.1", "@ionic/angular": "^5.5.2", "rxjs": "~6.6.0", "tslib": "^2.0.0", @@ -36,6 +34,7 @@ "@types/jasmine": "~3.6.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", + "co.mylonas.cordova.applicationstate": "github:leomylonas/cordova-plugin-applicationstate", "codelyzer": "^6.0.0", "cordova-android": "^9.0.0", "cordova-plugin-app-exit": "0.0.2", @@ -47,8 +46,6 @@ "cordova-plugin-ionic-keyboard": "^2.2.0", "cordova-plugin-ionic-webview": "^4.2.1", "cordova-plugin-local-notification": "^0.9.0-beta.2", - "cordova-plugin-network-information": "^2.0.2", - "cordova-plugin-powermanagement-orig": "^1.1.2", "cordova-plugin-splashscreen": "^5.0.2", "cordova-plugin-statusbar": "^2.4.2", "cordova-plugin-whitelist": "^1.3.3", @@ -1777,30 +1774,6 @@ "@types/cordova": "^0.0.34" } }, - "node_modules/@ionic-native/network": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-5.31.1.tgz", - "integrity": "sha512-dSN7jaiXXgm8kVcvxcD39wWPFg2NujWTrEnj9Fq7Fx2aGKkxPkocNsxoy02aFVyBp1LQZvp+uiDNmVIVRjrA7A==", - "dependencies": { - "@types/cordova": "latest" - }, - "peerDependencies": { - "@ionic-native/core": "^5.1.0", - "rxjs": "^5.5.0 || ^6.5.0" - } - }, - "node_modules/@ionic-native/power-management": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/@ionic-native/power-management/-/power-management-5.31.1.tgz", - "integrity": "sha512-SJiTOMMkEEJ7B/MNztCjnwCKEF416hRDLquSeK6zJbRhPzMsWLr7Cb+BzjvwV5pfo05ilfzV08yB95IBdRteUg==", - "dependencies": { - "@types/cordova": "latest" - }, - "peerDependencies": { - "@ionic-native/core": "^5.1.0", - "rxjs": "^5.5.0 || ^6.5.0" - } - }, "node_modules/@ionic/angular": { "version": "5.5.5", "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.5.5.tgz", @@ -3962,6 +3935,18 @@ "node": ">=6" } }, + "node_modules/co.mylonas.cordova.applicationstate": { + "version": "0.0.2", + "resolved": "git+ssh://git@github.com/leomylonas/cordova-plugin-applicationstate.git#1ead7d5045eed494bd1bb8dabde461208634780b", + "dev": true, + "engines": [ + { + "name": "cordova", + "version": ">=3.0.0" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -4891,25 +4876,6 @@ } ] }, - "node_modules/cordova-plugin-network-information": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-network-information/-/cordova-plugin-network-information-2.0.2.tgz", - "integrity": "sha512-NwO3qDBNL/vJxUxBTPNOA1HvkDf9eTeGH8JSZiwy1jq2W2mJKQEDBwqWkaEQS19Yd/MQTiw0cykxg5D7u4J6cQ==", - "dev": true, - "engines": { - "cordovaDependencies": { - "3.0.0": { - "cordova": ">100" - } - } - } - }, - "node_modules/cordova-plugin-powermanagement-orig": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-powermanagement-orig/-/cordova-plugin-powermanagement-orig-1.1.2.tgz", - "integrity": "sha1-hVuljKvnndWxECXkWG/ywe0YnOA=", - "dev": true - }, "node_modules/cordova-plugin-splashscreen": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-5.0.2.tgz", @@ -20432,22 +20398,6 @@ "@types/cordova": "^0.0.34" } }, - "@ionic-native/network": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-5.31.1.tgz", - "integrity": "sha512-dSN7jaiXXgm8kVcvxcD39wWPFg2NujWTrEnj9Fq7Fx2aGKkxPkocNsxoy02aFVyBp1LQZvp+uiDNmVIVRjrA7A==", - "requires": { - "@types/cordova": "latest" - } - }, - "@ionic-native/power-management": { - "version": "5.31.1", - "resolved": "https://registry.npmjs.org/@ionic-native/power-management/-/power-management-5.31.1.tgz", - "integrity": "sha512-SJiTOMMkEEJ7B/MNztCjnwCKEF416hRDLquSeK6zJbRhPzMsWLr7Cb+BzjvwV5pfo05ilfzV08yB95IBdRteUg==", - "requires": { - "@types/cordova": "latest" - } - }, "@ionic/angular": { "version": "5.5.5", "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.5.5.tgz", @@ -22330,6 +22280,11 @@ "shallow-clone": "^3.0.0" } }, + "co.mylonas.cordova.applicationstate": { + "version": "git+ssh://git@github.com/leomylonas/cordova-plugin-applicationstate.git#1ead7d5045eed494bd1bb8dabde461208634780b", + "dev": true, + "from": "co.mylonas.cordova.applicationstate@github:leomylonas/cordova-plugin-applicationstate" + }, "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -23070,18 +23025,6 @@ "integrity": "sha512-63n77K1pt8dnbWnNR8QWETi9Glezi1bvNHvHWmGNIOv0xCb0phZnm+Ku49BQ+omwe8Z5voMvrA4I03SYPpv38w==", "dev": true }, - "cordova-plugin-network-information": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-network-information/-/cordova-plugin-network-information-2.0.2.tgz", - "integrity": "sha512-NwO3qDBNL/vJxUxBTPNOA1HvkDf9eTeGH8JSZiwy1jq2W2mJKQEDBwqWkaEQS19Yd/MQTiw0cykxg5D7u4J6cQ==", - "dev": true - }, - "cordova-plugin-powermanagement-orig": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cordova-plugin-powermanagement-orig/-/cordova-plugin-powermanagement-orig-1.1.2.tgz", - "integrity": "sha1-hVuljKvnndWxECXkWG/ywe0YnOA=", - "dev": true - }, "cordova-plugin-splashscreen": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-5.0.2.tgz", diff --git a/package.json b/package.json index 0811f9f..dd25295 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@types/jasmine": "~3.6.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", + "co.mylonas.cordova.applicationstate": "github:leomylonas/cordova-plugin-applicationstate", "codelyzer": "^6.0.0", "cordova-android": "^9.0.0", "cordova-plugin-app-exit": "0.0.2", diff --git a/resources/android/icon/drawable-hdpi-icon.png b/resources/android/icon/drawable-hdpi-icon.png index 0913286..e97cc48 100644 Binary files a/resources/android/icon/drawable-hdpi-icon.png and b/resources/android/icon/drawable-hdpi-icon.png differ diff --git a/resources/android/icon/drawable-ldpi-icon.png b/resources/android/icon/drawable-ldpi-icon.png index 9aa7982..2b5a9f2 100644 Binary files a/resources/android/icon/drawable-ldpi-icon.png and b/resources/android/icon/drawable-ldpi-icon.png differ diff --git a/resources/android/icon/drawable-mdpi-icon.png b/resources/android/icon/drawable-mdpi-icon.png index f630706..f04d49d 100644 Binary files a/resources/android/icon/drawable-mdpi-icon.png and b/resources/android/icon/drawable-mdpi-icon.png differ diff --git a/resources/android/icon/drawable-xhdpi-icon.png b/resources/android/icon/drawable-xhdpi-icon.png index 717b017..68fea38 100644 Binary files a/resources/android/icon/drawable-xhdpi-icon.png and b/resources/android/icon/drawable-xhdpi-icon.png differ diff --git a/resources/android/icon/drawable-xxhdpi-icon.png b/resources/android/icon/drawable-xxhdpi-icon.png index cb7d456..0e6b7f3 100644 Binary files a/resources/android/icon/drawable-xxhdpi-icon.png and b/resources/android/icon/drawable-xxhdpi-icon.png differ diff --git a/resources/android/icon/drawable-xxxhdpi-icon.png b/resources/android/icon/drawable-xxxhdpi-icon.png index 53d8b39..46f534b 100644 Binary files a/resources/android/icon/drawable-xxxhdpi-icon.png and b/resources/android/icon/drawable-xxxhdpi-icon.png differ diff --git a/src/app/api.service.ts b/src/app/api.service.ts index 4dd5562..6886087 100644 --- a/src/app/api.service.ts +++ b/src/app/api.service.ts @@ -5,6 +5,7 @@ import {Token} from './token'; import {Setting} from './setting'; import {ChatMessage} from './chat.message'; import {ChatTokenResponse} from './chat.token'; +import {VersionInfo} from './version.info'; @Injectable({ providedIn: 'root' @@ -15,15 +16,6 @@ export class ApiService { constructor(private client: HttpClient) { } - storeData(key: string, value: string): void - { - sessionStorage.setItem(key, value); - } - - getFromStorage(key: string): string { - return sessionStorage.getItem(key); - } - getAuthToken(username: string, password: string): Observable { return this.client.post(Setting.URL + '/token', {username, password}); } @@ -56,4 +48,9 @@ export class ApiService { {headers: {Authorization: 'Bearer ' + token}} ); } + + getCurrentVersion(): Observable + { + return this.client.get(Setting.URL + '/metasocket/info'); + } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 01abf81..e1d163a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { ApiService } from './api.service'; +import AppStorage from './app.storage'; @Component({ selector: 'app-root', @@ -13,7 +14,7 @@ export class AppComponent { public constructor(private apiService: ApiService) { - AppComponent.token = this.apiService.getFromStorage('token'); + AppComponent.token = AppStorage.getToken(); } public getToken(): string diff --git a/src/app/app.config.ts b/src/app/app.config.ts new file mode 100644 index 0000000..d30c8f7 --- /dev/null +++ b/src/app/app.config.ts @@ -0,0 +1,4 @@ +export default class AppConfig +{ + public static readonly VERSION: string = '1.5.0'; +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index be80cab..72e6fde 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,14 +1,11 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { RouteReuseStrategy } from '@angular/router'; - -import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; - -import { FormsModule } from '@angular/forms'; -import { HttpClientModule } from '@angular/common/http'; - -import { AppComponent } from './app.component'; -import { AppRoutingModule } from './app-routing.module'; +import {NgModule} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {RouteReuseStrategy} from '@angular/router'; +import {IonicModule, IonicRouteStrategy} from '@ionic/angular'; +import {FormsModule} from '@angular/forms'; +import {HttpClientModule} from '@angular/common/http'; +import {AppComponent} from './app.component'; +import {AppRoutingModule} from './app-routing.module'; import {ChatComponent} from './chat/chat.component'; import {LoginComponent} from './login/login.component'; import {TopbarComponent} from './topbar/topbar.component'; diff --git a/src/app/app.storage.ts b/src/app/app.storage.ts new file mode 100644 index 0000000..d26d50d --- /dev/null +++ b/src/app/app.storage.ts @@ -0,0 +1,77 @@ +import {ApiService} from './api.service'; + +export default class AppStorage +{ + private static readonly LAST_VERSION_CHECK: string = 'lastVersionCheck'; + private static readonly IS_UPDATE_NOTIFICATION_DESIRED: string = 'isUpdateNotificationDesired'; + private static readonly TOKEN: string = 'token'; + private static readonly USER_ID: string = 'userId'; + private static readonly CHAT_TOKEN: string = 'chatToken'; + private static readonly LAST_LOGIN_NAME: string = 'lastLoginName'; + + public static getChatToken(): string + { + return sessionStorage.getItem(this.CHAT_TOKEN); + } + + public static getLastLoginName(): string + { + return localStorage.getItem(this.LAST_LOGIN_NAME); + } + + public static getLastVersionCheck(): number + { + const lastCheck = localStorage.getItem(this.LAST_VERSION_CHECK); + + return lastCheck === null ? null : Number(lastCheck); + } + + public static getToken(): string + { + return sessionStorage.getItem(this.TOKEN); + } + + public static getUserId(): number + { + const userId = sessionStorage.getItem(this.USER_ID); + + return userId === null ? null : Number(userId); + } + + public static isUpdateNotificationDesired(): boolean + { + const isDesired = localStorage.getItem(this.IS_UPDATE_NOTIFICATION_DESIRED); + + return isDesired === null ? true : isDesired === 'true'; + } + + public static setChatToken(chatToken: string): void + { + sessionStorage.setItem(this.CHAT_TOKEN, chatToken); + } + + public static setIsUpdateNotificationDesired(isDesired: boolean): void + { + localStorage.setItem(this.IS_UPDATE_NOTIFICATION_DESIRED, isDesired.toString()); + } + + public static setLastLoginName(loginName: string): void + { + localStorage.setItem(this.LAST_LOGIN_NAME, loginName); + } + + public static setLastVersionCheck(versionCheck: number): void + { + localStorage.setItem(this.LAST_VERSION_CHECK, versionCheck.toString()); + } + + public static setToken(token: string): void + { + sessionStorage.setItem(this.TOKEN, token); + } + + public static setUserId(userId: number): void + { + sessionStorage.setItem(this.USER_ID, userId.toString()); + } +} diff --git a/src/app/chat/chat.component.ts b/src/app/chat/chat.component.ts index 4519cf7..24a58a2 100644 --- a/src/app/chat/chat.component.ts +++ b/src/app/chat/chat.component.ts @@ -9,6 +9,12 @@ import {LocalNotifications} from '@ionic-native/local-notifications/ngx'; import {BackgroundMode} from '@ionic-native/background-mode/ngx'; import {AppComponent} from '../app.component'; import {Platform} from '@ionic/angular'; +import {VersionInfo} from '../version.info'; +import FullscreenError from '../fullscreen.error'; +import FullscreenNotification from '../fullscreen.notification'; +import AppConfig from '../app.config'; +import FullscreenDialog from '../fullscreen.dialog'; +import AppStorage from '../app.storage'; const {App} = Plugins; @@ -44,11 +50,12 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W private backgroundMode: BackgroundMode, private platform: Platform ) { - this.userToken = this.apiService.getFromStorage('token'); - this.userId = Number(this.apiService.getFromStorage('userId')); + this.userToken = AppStorage.getToken(); + this.userId = AppStorage.getUserId(); this.url = Setting.URL; this.websocketService.setListener(this); - this.websocketService.initializeSocket(this.apiService.getFromStorage('chatToken')); + this.websocketService.initializeSocket(AppStorage.getChatToken()); + this.backgroundMode.enable(); this.backgroundMode.disableBatteryOptimizations(); this.backgroundMode.disableWebViewOptimizations(); } @@ -94,7 +101,7 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W } ).catch( (error) => { - window.alert('Fehler ' + error.status + ': Verbindung zur Web-API gescheitert!'); + const notification = new FullscreenError('Verbindung zur Web-API gescheitert!\n\nStatus-Code: ' + error.status); } ); @@ -104,9 +111,9 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W this.platform.backButton.subscribe( () => { - if (window.confirm('Möchtest du wirklich ausloggen?')) { - this.onLogout(); - } + const question = new FullscreenDialog('App beenden', 'Möchtest du wirklich ausloggen?'); + question.addButton('Ja', () => {this.onLogout(); }); + question.addButton('Nein'); } ); @@ -115,6 +122,13 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W this.websocketService.sendKeepAliveMessage(); }, 1000 * 60 // every minute ); + + setTimeout( + () => { + this.checkVersion(); + }, + 5000 + ); } onScroll(): void { @@ -161,7 +175,7 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W } onLogout() { - this.apiService.storeData('token', null); + AppStorage.setToken(null); this.websocketService.destroy(); AppComponent.token = null; @@ -173,7 +187,7 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W this.messages.push(message); this.messageOffset++; - if (this.hasFocus || message.userId === this.userId) { + if (!this.backgroundMode.isActive() || message.userId === this.userId) { return; } @@ -263,6 +277,46 @@ export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, W ); } + private checkVersion(): void + { + const lastChecked = AppStorage.getLastVersionCheck(); + const today = new Date(); + + if (lastChecked !== null) { + const dateLastChecked = new Date(lastChecked); + + if (dateLastChecked.toDateString() === today.toDateString()) { + return; + } + } + + AppStorage.setLastVersionCheck(today.getTime()); + + this.apiService.getCurrentVersion().toPromise() + .then( + (versionInfo: VersionInfo) => { + const isUpdateNotificationDesired = AppStorage.isUpdateNotificationDesired(); + + if (isUpdateNotificationDesired && AppConfig.VERSION < versionInfo.currentVersionAndroid) { + const notification = new FullscreenNotification('Neue Version', 'Eine neue Version von METAsocket ist verfügbar.'); + const label = document.createElement('label'); + const checkbox = document.createElement('input'); + const text = document.createElement('span'); + text.innerText = ' Nerv mich nie wieder damit'; + checkbox.type = 'checkbox'; + label.appendChild(checkbox); + label.appendChild(text); + label.onclick = () => { + AppStorage.setIsUpdateNotificationDesired(!checkbox.checked); + }; + + notification.addExtendedInputElement(label); + notification.addUrlButton('Runterladen', 'https://sabolli.de/wow/metasocket/download/android'); + } + } + ); + } + private hasChatMessage(message: ChatMessage): boolean { for (const messageStored of this.messages) { diff --git a/src/app/fullscreen.dialog.ts b/src/app/fullscreen.dialog.ts new file mode 100644 index 0000000..aab93fb --- /dev/null +++ b/src/app/fullscreen.dialog.ts @@ -0,0 +1,73 @@ +export default class FullscreenDialog +{ + protected rootElement: HTMLDivElement; + protected window: HTMLDivElement; + protected title: HTMLHeadElement; + protected message: HTMLParagraphElement; + protected extendedInputArea: HTMLDivElement; + protected buttonArea: HTMLDivElement; + + public constructor(title: string, message: string) { + this.rootElement = document.createElement('div'); + this.rootElement.classList.add('notification'); + + this.window = document.createElement('div'); + this.window.classList.add('notification-window'); + this.rootElement.appendChild(this.window); + + this.title = document.createElement('h1'); + this.title.classList.add('notification-headline'); + this.title.innerText = title; + this.window.appendChild(this.title); + + this.message = document.createElement('p'); + this.message.innerText = message; + this.window.appendChild(this.message); + + this.extendedInputArea = document.createElement('div'); + this.extendedInputArea.classList.add('notification-extended-input-area'); + this.window.appendChild(this.extendedInputArea); + + this.buttonArea = document.createElement('div'); + this.buttonArea.classList.add('notification-button-area'); + this.window.appendChild(this.buttonArea); + + document.body.appendChild(this.rootElement); + } + + public addButton(title: string, callback: () => void = () => {}): void + { + const button = document.createElement('button'); + + button.classList.add('notification-button'); + button.innerText = title; + button.onclick = () => { + callback(); + this.close(); + }; + + this.buttonArea.appendChild(button); + } + + public addUrlButton(title: string, url: string): void + { + const button = document.createElement('a'); + + button.classList.add('notification-button'); + button.innerText = title; + button.href = url; + button.onclick = () => {this.close(); }; + + this.buttonArea.appendChild(button); + } + + public addExtendedInputElement(element: HTMLElement): void + { + this.extendedInputArea.appendChild(element); + } + + public close(): void + { + this.rootElement.remove(); + } +} diff --git a/src/app/fullscreen.error.ts b/src/app/fullscreen.error.ts new file mode 100644 index 0000000..4831150 --- /dev/null +++ b/src/app/fullscreen.error.ts @@ -0,0 +1,9 @@ +import FullscreenNotification from './fullscreen.notification'; + +export default class FullscreenError extends FullscreenNotification +{ + public constructor(message: string) { + super('Fehler', message); + this.window.classList.add('notification-window-error'); + } +} diff --git a/src/app/fullscreen.notification.ts b/src/app/fullscreen.notification.ts new file mode 100644 index 0000000..52c94c9 --- /dev/null +++ b/src/app/fullscreen.notification.ts @@ -0,0 +1,10 @@ +import FullscreenDialog from './fullscreen.dialog'; + +export default class FullscreenNotification extends FullscreenDialog +{ + public constructor(title: string, message: string) { + super(title, message); + + this.addButton('OK'); + } +} diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html index d5bd6aa..f7b222f 100644 --- a/src/app/login/login.component.html +++ b/src/app/login/login.component.html @@ -6,15 +6,17 @@
+ +

{{version}}

diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 1957a47..c64021f 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import { ApiService } from '../api.service'; import {Platform} from '@ionic/angular'; import {AppMinimize} from '@ionic-native/app-minimize/ngx'; +import AppStorage from '../app.storage'; +import AppConfig from '../app.config'; @Component({ selector: 'app-login', @@ -12,8 +14,14 @@ export class LoginComponent implements OnInit { username = ''; password = ''; error: string = null; + version: string; - constructor(private apiService: ApiService, private platform: Platform, private appMinimize: AppMinimize) { } + @ViewChild('loginName') usernameElement: ElementRef; + @ViewChild('loginPassword') passwordElement: ElementRef; + + constructor(private apiService: ApiService, private platform: Platform, private appMinimize: AppMinimize) { + this.version = AppConfig.VERSION; + } ngOnInit(): void { this.platform.backButton.subscribe( @@ -21,6 +29,12 @@ export class LoginComponent implements OnInit { this.appMinimize.minimize(); } ); + + const lastUsername = AppStorage.getLastLoginName() ?? ''; + + if (lastUsername.length > 0) { + this.username = lastUsername; + } } login(event): void @@ -31,19 +45,20 @@ export class LoginComponent implements OnInit { this.apiService.getAuthToken(this.username, this.password).toPromise() .then( (response) => { - this.apiService.storeData('token', response.token); + AppStorage.setToken(response.token); this.apiService.getChatToken(response.token).toPromise() .then( (chatTokenResponse) => { - this.apiService.storeData('chatToken', chatTokenResponse.token); - this.apiService.storeData('userId', chatTokenResponse.userId.toString()); + AppStorage.setChatToken(chatTokenResponse.token); + AppStorage.setUserId(chatTokenResponse.userId); + AppStorage.setLastLoginName(this.username); window.location.reload(); } ).catch( (error) => { - this.apiService.storeData('token', null); + AppStorage.setToken(null); this.error = error.name + ': ' + error.message; event.target.disabled = false; @@ -53,7 +68,7 @@ export class LoginComponent implements OnInit { } ).catch ( (error) => { - this.apiService.storeData('token', null); + AppStorage.setToken(null); switch (error.status) { case 401: @@ -76,7 +91,7 @@ export class LoginComponent implements OnInit { this.error = 'Fehler: Etwas unfassbar grauenhaftes ist passiert!'; } - console.log(error); + this.password = ''; event.target.disabled = false; event.target.style.visibility = 'visible'; diff --git a/src/app/topbar/topbar.component.ts b/src/app/topbar/topbar.component.ts index 35173b5..989ef9a 100644 --- a/src/app/topbar/topbar.component.ts +++ b/src/app/topbar/topbar.component.ts @@ -1,7 +1,7 @@ import {Component, Input, OnInit} from '@angular/core'; import {ApiService} from '../api.service'; import {AppComponent} from '../app.component'; -import {ForegroundService} from '@ionic-native/foreground-service/ngx'; +import AppStorage from '../app.storage'; @Component({ selector: 'app-topbar', @@ -11,21 +11,20 @@ import {ForegroundService} from '@ionic-native/foreground-service/ngx'; export class TopbarComponent implements OnInit { @Input() token: string; - constructor(private apiService: ApiService, private foregroundService: ForegroundService) { + constructor(private apiService: ApiService) { } ngOnInit(): void { } logout(): void { - this.apiService.deleteAuthToken(this.apiService.getFromStorage('token')).toPromise() + this.apiService.deleteAuthToken(AppStorage.getToken()).toPromise() .then() .catch( (error) => {console.log(error); } ); - this.apiService.storeData('token', null); - this.foregroundService.stop(); + AppStorage.setToken(null); AppComponent.token = null; } } diff --git a/src/app/version.info.ts b/src/app/version.info.ts new file mode 100644 index 0000000..b2d1b61 --- /dev/null +++ b/src/app/version.info.ts @@ -0,0 +1,5 @@ +export interface VersionInfo +{ + success: boolean; + currentVersionAndroid: string; +} diff --git a/src/global.scss b/src/global.scss index eaf53b4..c6c65cc 100644 --- a/src/global.scss +++ b/src/global.scss @@ -3,6 +3,15 @@ to { top: 0} } +@keyframes fullscreen-notification-entrance { + from { background-color: rgba(0, 0, 0, 0) } + to { background-color: rgba(0, 0, 0, 0.5) } +} + +@keyframes fullscreen-notification-window-entrance { + from { transform: scale(0)} +} + * { box-sizing: border-box; font-family: sans-serif; @@ -158,6 +167,14 @@ html, body { #login-form input[type=submit] { margin-top: 50px; + background-color: white; + color: #2a2a2a; + font-weight: bold; +} + +.app-version { + color: white; + margin-top: 50px; } .error-message { @@ -247,6 +264,64 @@ html, body { background-color: #550000; } +.notification { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 10; + display: flex; + justify-content: center; + align-items: center; + color: white; + animation-name: fullscreen-notification-entrance; + animation-duration: 0.5s; +} + +.notification-window { + background-color: #251a25; + padding: 20px; + margin: 20px; + box-shadow: 0 0 20px black; + animation-name: fullscreen-notification-window-entrance; + animation-duration: 0.5s; +} + +.notification-window-error { + background-color: #bb0000; +} + +.notification-button-area { + display: flex; + justify-content: space-around; + margin-top: 30px; +} + +.notification-button { + background-color: white; + color: #251a25; + font-weight: bold; + padding: 5px 20px; + border: none; + border-radius: 0; + cursor: pointer; +} + +input[type="checkbox"] { + border: solid 1px white !important; + border-radius: 0 !important; +} + +a.notification-button { + text-decoration: none; +} + +.notification-headline { + margin-top: 5px; +} + @media only screen and (max-height: 500px) { #login { position: static;