diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2977461 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +install: + ionic cordova plugin add cordova-plugin-local-notification + npm install --save @ionic-native/local-notifications + npm install --save @ionic-native/core + npm install --save @capacitor/core + +android: + ionic build + sudo archlinux-java set java-8-openjdk + ANDROID_SDK_ROOT=/home/mal/Android/Sdk JAVA_HOME=/usr/lib/jvm/java-8-openjdk/ ionic cordova build android + sudo archlinux-java set java-11-openjdk + +deployment: + read VERSION + cp platforms/android/app/build/outputs/apk/debug/app-debug.apk '/run/user/1000/gvfs/mtp:host=Fairphone_FP2_ff87fc9d/Interner gemeinsamer Speicher/bluetooth/metasocket-${VERSION}.apk' + + diff --git a/build/android/build.bash b/build/android/build.bash new file mode 100755 index 0000000..62c0dab --- /dev/null +++ b/build/android/build.bash @@ -0,0 +1,60 @@ +#/bin/bash + +TARGET='/home/mal/Code/Ionic/metasocket-cordova/resources/android' +TARGET_RESOURCES=${TARGET} +TARGET_JAVA=/home/mal/Code/Ionic/metasocket-cordova/platforms/android/app/java/com/metasocket/app + +RESOURCES='/home/mal/Code/Ionic/metasocket-cordova/build/android' + +# SPLASH LANDSCAPE +inkscape ${RESOURCES}/splash-drawable-480x320.svg -o ${TARGET_RESOURCES}/splash/drawable-land-ldpi-screen.png -w 320 -h 240 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-800x480.svg -o ${TARGET_RESOURCES}/splash/drawable-land-hdpi-screen.png -w 800 -h 480 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-480x320.svg -o ${TARGET_RESOURCES}/splash/drawable-land-mdpi-screen.png -w 480 -h 320 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-1280x720.svg -o ${TARGET_RESOURCES}/splash/drawable-land-xhdpi-screen.png -w 1280 -h 720 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-1600x960.svg -o ${TARGET_RESOURCES}/splash/drawable-land-xxhdpi-screen.png -w 1600 -h 960 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-1920x1280.svg -o ${TARGET_RESOURCES}/splash/drawable-land-xxxhdpi-screen.png -w 1920 -h 1280 --export-overwrite + +# SPLASH PORTRAIT +inkscape ${RESOURCES}/splash-drawable-320x480.svg -o ${TARGET_RESOURCES}/splash/drawable-port-ldpi-screen.png -w 240 -h 320 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-480x800.svg -o ${TARGET_RESOURCES}/splash/drawable-port-hdpi-screen.png -w 480 -h 800 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-320x480.svg -o ${TARGET_RESOURCES}/splash/drawable-port-mdpi-screen.png -w 320 -h 480 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-720x1280.svg -o ${TARGET_RESOURCES}/splash/drawable-port-xhdpi-screen.png -w 720 -h 1280 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-960x1600.svg -o ${TARGET_RESOURCES}/splash/drawable-port-xxhdpi-screen.png -w 960 -h 1600 --export-overwrite +inkscape ${RESOURCES}/splash-drawable-1280x1920.svg -o ${TARGET_RESOURCES}/splash/drawable-port-xxxhdpi-screen.png -w 1280 -h 1920 --export-overwrite + +# ICONS +inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/icon/drawable-ldpi-icon.png -w 36 -h 36 --export-overwrite + +inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/icon/drawable-hdpi-icon.png -w 72 -h 72 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher_round.svg -o ${TARGET_RESOURCES}/icon/drawable-mipmap-hdpi/ic_launcher_round.png -w 72 -h 72 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/mipmap-hdpi/ic_launcher_foreground.png -w 162 -h 162 --export-overwrite + +inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/icon/drawable-mdpi-icon.png -w 48 -h 48 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher_round.svg -o ${TARGET_RESOURCES}/mipmap-mdpi/ic_launcher_round.png -w 48 -h 48 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/mipmap-mdpi/ic_launcher_foreground.png -w 108 -h 108 --export-overwrite + +inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/icon/drawable-xhdpi-icon.png -w 96 -h 96 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher_round.svg -o ${TARGET_RESOURCES}/mipmap-xhdpi/ic_launcher_round.png -w 96 -h 96 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/mipmap-xhdpi/ic_launcher_foreground.png -w 216 -h 216 --export-overwrite + +inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/icon/drawable-xxhdpi-icon.png -w 144 -h 144 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher_round.svg -o ${TARGET_RESOURCES}/mipmap-xxhdpi/ic_launcher_round.png -w 144 -h 144 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/mipmap-xxhdpi/ic_launcher_foreground.png -w 324 -h 324 --export-overwrite + +inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/icon/drawable-xxxhdpi-icon.png -w 192 -h 192 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher_round.svg -o ${TARGET_RESOURCES}/mipmap-xxxhdpi/ic_launcher_round.png -w 192 -h 192 --export-overwrite +# inkscape ${RESOURCES}/ic_launcher.svg -o ${TARGET_RESOURCES}/mipmap-xxxhdpi/ic_launcher_foreground.png -w 432 -h 432 --export-overwrite + +exit 0 + +# cp -R ${RESOURCES}/res/* ${TARGET_RESOURCES}/ + +# SOUND +mkdir -p ${TARGET_RESOURCES}/raw +cp ${RESOURCES}/murloc.wav ${TARGET_RESOURCES}/raw/ + +# OVERRIDE MainActivity.java +# cp ${RESOURCES}/MainActivity.java ${TARGET_JAVA}/ + +# OVERRIDE AndroidManifest.xml +cp ${RESOURCES}/AndroidManifest.xml ${TARGET}/ diff --git a/build/android/ic_launcher.svg b/build/android/ic_launcher.svg new file mode 100644 index 0000000..d077488 --- /dev/null +++ b/build/android/ic_launcher.svg @@ -0,0 +1,68 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/ic_launcher_round.svg b/build/android/ic_launcher_round.svg new file mode 100644 index 0000000..d6ce0e6 --- /dev/null +++ b/build/android/ic_launcher_round.svg @@ -0,0 +1,67 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/notification-icon-disabled.svg b/build/android/notification-icon-disabled.svg new file mode 100644 index 0000000..bdcc628 --- /dev/null +++ b/build/android/notification-icon-disabled.svg @@ -0,0 +1,62 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/build/android/notification-icon-enabled.svg b/build/android/notification-icon-enabled.svg new file mode 100644 index 0000000..c87d643 --- /dev/null +++ b/build/android/notification-icon-enabled.svg @@ -0,0 +1,62 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/build/android/res/drawable-hdpi/ic_stat_notification_icon_enabled.png b/build/android/res/drawable-hdpi/ic_stat_notification_icon_enabled.png new file mode 100644 index 0000000..a0ec5c7 Binary files /dev/null and b/build/android/res/drawable-hdpi/ic_stat_notification_icon_enabled.png differ diff --git a/build/android/res/drawable-mdpi/ic_stat_notification_icon_enabled.png b/build/android/res/drawable-mdpi/ic_stat_notification_icon_enabled.png new file mode 100644 index 0000000..8c08017 Binary files /dev/null and b/build/android/res/drawable-mdpi/ic_stat_notification_icon_enabled.png differ diff --git a/build/android/res/drawable-xhdpi/ic_stat_notification_icon_enabled.png b/build/android/res/drawable-xhdpi/ic_stat_notification_icon_enabled.png new file mode 100644 index 0000000..91b3910 Binary files /dev/null and b/build/android/res/drawable-xhdpi/ic_stat_notification_icon_enabled.png differ diff --git a/build/android/res/drawable-xxhdpi/ic_stat_notification_icon_enabled.png b/build/android/res/drawable-xxhdpi/ic_stat_notification_icon_enabled.png new file mode 100644 index 0000000..80d7209 Binary files /dev/null and b/build/android/res/drawable-xxhdpi/ic_stat_notification_icon_enabled.png differ diff --git a/build/android/res/drawable-xxxhdpi/ic_stat_notification_icon_enabled.png b/build/android/res/drawable-xxxhdpi/ic_stat_notification_icon_enabled.png new file mode 100644 index 0000000..1916b86 Binary files /dev/null and b/build/android/res/drawable-xxxhdpi/ic_stat_notification_icon_enabled.png differ diff --git a/build/android/splash-drawable-1280x1920.svg b/build/android/splash-drawable-1280x1920.svg new file mode 100644 index 0000000..4e75fae --- /dev/null +++ b/build/android/splash-drawable-1280x1920.svg @@ -0,0 +1,73 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-1280x720.svg b/build/android/splash-drawable-1280x720.svg new file mode 100644 index 0000000..b56de82 --- /dev/null +++ b/build/android/splash-drawable-1280x720.svg @@ -0,0 +1,72 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-1600x960.svg b/build/android/splash-drawable-1600x960.svg new file mode 100644 index 0000000..b6e1ae4 --- /dev/null +++ b/build/android/splash-drawable-1600x960.svg @@ -0,0 +1,72 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-1920x1280.svg b/build/android/splash-drawable-1920x1280.svg new file mode 100644 index 0000000..4a6b202 --- /dev/null +++ b/build/android/splash-drawable-1920x1280.svg @@ -0,0 +1,72 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-320x480.svg b/build/android/splash-drawable-320x480.svg new file mode 100644 index 0000000..8ab1169 --- /dev/null +++ b/build/android/splash-drawable-320x480.svg @@ -0,0 +1,69 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-480x320.svg b/build/android/splash-drawable-480x320.svg new file mode 100644 index 0000000..97a80b8 --- /dev/null +++ b/build/android/splash-drawable-480x320.svg @@ -0,0 +1,68 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-480x800.svg b/build/android/splash-drawable-480x800.svg new file mode 100644 index 0000000..f95b925 --- /dev/null +++ b/build/android/splash-drawable-480x800.svg @@ -0,0 +1,73 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-720x1280.svg b/build/android/splash-drawable-720x1280.svg new file mode 100644 index 0000000..77993e5 --- /dev/null +++ b/build/android/splash-drawable-720x1280.svg @@ -0,0 +1,73 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-800x480.svg b/build/android/splash-drawable-800x480.svg new file mode 100644 index 0000000..9adec9d --- /dev/null +++ b/build/android/splash-drawable-800x480.svg @@ -0,0 +1,72 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash-drawable-960x1600.svg b/build/android/splash-drawable-960x1600.svg new file mode 100644 index 0000000..d3ad235 --- /dev/null +++ b/build/android/splash-drawable-960x1600.svg @@ -0,0 +1,73 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/android/splash.svg b/build/android/splash.svg new file mode 100644 index 0000000..f516cba --- /dev/null +++ b/build/android/splash.svg @@ -0,0 +1,68 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/ios/AppIcon.svg b/build/ios/AppIcon.svg new file mode 100644 index 0000000..d077488 --- /dev/null +++ b/build/ios/AppIcon.svg @@ -0,0 +1,68 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/build/ios/build.bash b/build/ios/build.bash new file mode 100755 index 0000000..3672e14 --- /dev/null +++ b/build/ios/build.bash @@ -0,0 +1,33 @@ +#!/bin/bash + +TARGET='/home/mal/Code/Angular/metasocket/ios/App/App/Assets.xcassets' +RESOURCES='/home/mal/Code/Angular/metasocket/build/ios' + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-20x20@1x.png -w 20 -h 20 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-20x20@2x.png -w 40 -h 40 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-20x20@2x-1.png -w 40 -h 40 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-20x20@3x.png -w 60 -h 60 --export-overwrite + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-29x29@1x.png -w 29 -h 29 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-29x29@2x.png -w 58 -h 58 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-29x29@2x-1.png -w 58 -h 58 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-29x29@3x.png -w 87 -h 87 --export-overwrite + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-40x40@1x.png -w 40 -h 40 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-40x40@2x.png -w 80 -h 80 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-40x40@2x-1.png -w 80 -h 80 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-40x40@3x.png -w 120 -h 120 --export-overwrite + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-60x60@2x.png -w 120 -h 120 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-60x60@3x.png -w 180 -h 180 --export-overwrite + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-76x76@1x.png -w 76 -h 76 --export-overwrite +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-76x76@2x.png -w 152 -h 152 --export-overwrite + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png -w 167 -h 167 --export-overwrite + +inkscape ${RESOURCES}/AppIcon.svg -o ${TARGET}/AppIcon.appiconset/AppIcon-512@2x.png -w 1024 -h 1024 --export-overwrite + +inkscape ${RESOURCES}/splash.svg -o ${TARGET}/Splash.imageset/splash-2732x2732.png -w 2732 -h 2732 --export-overwrite +inkscape ${RESOURCES}/splash.svg -o ${TARGET}/Splash.imageset/splash-2732x2732-1.png -w 2732 -h 2732 --export-overwrite +inkscape ${RESOURCES}/splash.svg -o ${TARGET}/Splash.imageset/splash-2732x2732-2.png -w 2732 -h 2732 --export-overwrite diff --git a/build/ios/splash.svg b/build/ios/splash.svg new file mode 100644 index 0000000..3a9cc2e --- /dev/null +++ b/build/ios/splash.svg @@ -0,0 +1,68 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..3e6546d --- /dev/null +++ b/config.xml @@ -0,0 +1,101 @@ + + + METAsocket + WowApp's awesome instant messenger + Ionic Framework Team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ionic.config.json b/ionic.config.json index b8710bf..8bd5282 100644 --- a/ionic.config.json +++ b/ionic.config.json @@ -1,5 +1,7 @@ { "name": "metasocket-cordova", - "integrations": {}, + "integrations": { + "cordova": {} + }, "type": "angular" } diff --git a/package-lock.json b/package-lock.json index 4504aac..4fcbe94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1566,6 +1566,53 @@ "to-fast-properties": "^2.0.0" } }, + "@capacitor/core": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-2.4.6.tgz", + "integrity": "sha512-3KLSMorCELA5RNRXwHOGlRGuxXaxCEYHC29wOUxObicI2mf14hbMJWylt4QBzNmSqh3/ha7u4/CAZMoJUQR/QA==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@ionic-native/background-mode": { + "version": "5.31.1", + "resolved": "https://registry.npmjs.org/@ionic-native/background-mode/-/background-mode-5.31.1.tgz", + "integrity": "sha512-m5pCR2/QPblE5u9eqRC04qUyHkB0FzgD0a3/G6Q1Xx00BmuBeA/95BB2Htyrt3Ly/pp3W+ynTwljFMeDayi6bw==", + "requires": { + "@types/cordova": "^0.0.34" + } + }, + "@ionic-native/core": { + "version": "5.31.1", + "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-5.31.1.tgz", + "integrity": "sha512-dbJHezSuY8OqyFwyQiS+5QscA/BONhWitXgniljEblC5kQeLOCe+8p30JYHXj9xDciYzfqFP8ICmyaGOqUHJYw==", + "requires": { + "@types/cordova": "^0.0.34" + } + }, + "@ionic-native/foreground-service": { + "version": "5.31.1", + "resolved": "https://registry.npmjs.org/@ionic-native/foreground-service/-/foreground-service-5.31.1.tgz", + "integrity": "sha512-ln+2b9VgynZaemcAq+8v0BQ6Qxnx4veebVnAGsZ5fGzwqGIW9qMAbzncWJo9k7CK/jWDjZiqviIbKTxw4uPptQ==", + "requires": { + "@types/cordova": "^0.0.34" + } + }, + "@ionic-native/local-notifications": { + "version": "5.31.1", + "resolved": "https://registry.npmjs.org/@ionic-native/local-notifications/-/local-notifications-5.31.1.tgz", + "integrity": "sha512-OOd4EUnnfpDbvuJIwh3UzJ/6fFMTmf8f43mAfU0URkUS/LVJpl9aJzDCOLIB+0RXo+ZeRqW17q44qgUArgz1VQ==", + "requires": { + "@types/cordova": "^0.0.34" + } + }, "@ionic/angular": { "version": "5.5.5", "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.5.5.tgz", @@ -1646,6 +1693,25 @@ "schema-utils": "^2.7.0" } }, + "@netflix/nerror": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", + "integrity": "sha512-b+MGNyP9/LXkapreJzNUzcvuzZslj/RGgdVVJ16P2wSlYatfLycPObImqVJSmNAdyeShvNeM/pl3sVZsObFueg==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "extsprintf": "^1.4.0", + "lodash": "^4.17.15" + }, + "dependencies": { + "extsprintf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=", + "dev": true + } + } + }, "@ngtools/webpack": { "version": "11.1.4", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.1.4.tgz", @@ -1826,6 +1892,11 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@types/cordova": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", + "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=" + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -2215,6 +2286,29 @@ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, + "android-versions": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.6.0.tgz", + "integrity": "sha512-ojC2Ig7b/KJ6iNtR8e4bacmOsJyEkoERk3CKMIsnH7kJz5z6551NMbrVaRb7KXYavu1d74Uhml/bfcmqT3nAcg==", + "dev": true, + "requires": { + "semver": "^5.7.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", + "dev": true + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -2495,6 +2589,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -2679,6 +2779,12 @@ "tweetnacl": "^0.14.3" } }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "dev": true + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -2790,6 +2896,15 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3836,6 +3951,296 @@ } } }, + "cordova-android": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cordova-android/-/cordova-android-9.0.0.tgz", + "integrity": "sha512-2ZEgApK4LPMYW0zh/mLAH3CabzCaKE0yxQTzA2wTf0Eo2HHTJnRtDCf9spGf3nPOkubyXS6+pvzz5QzNHpVTqQ==", + "dev": true, + "requires": { + "android-versions": "^1.5.0", + "cordova-common": "^4.0.1", + "execa": "^4.0.2", + "fs-extra": "^9.0.1", + "nopt": "^4.0.3", + "properties-parser": "^0.3.1", + "which": "^2.0.2" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "cordova-common": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.0.2.tgz", + "integrity": "sha512-od7aNShyuBajzPY83mUEO8tERwwWdFklXETHiXP5Ft87CWeo/tSuwNPFztyTy8XYc74yXdogXKPTJeUHuVzB8Q==", + "dev": true, + "requires": { + "@netflix/nerror": "^1.1.3", + "ansi": "^0.3.1", + "bplist-parser": "^0.2.0", + "cross-spawn": "^7.0.1", + "elementtree": "^0.1.7", + "endent": "^1.4.1", + "fast-glob": "^3.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "plist": "^3.0.1", + "q": "^1.5.1", + "read-chunk": "^3.2.0", + "strip-bom": "^4.0.0", + "underscore": "^1.9.2" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "cordova-plugin-background-mode": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/cordova-plugin-background-mode/-/cordova-plugin-background-mode-0.7.3.tgz", + "integrity": "sha512-LsU1v7EgTUROaks+tcQ8TnMzVUcU/TwjDVwj2O/4e4aI2q2ldLGsiZorqKqfqvwh2HoIssmY73OwJk91hQi62w==", + "dev": true + }, + "cordova-plugin-badge": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/cordova-plugin-badge/-/cordova-plugin-badge-0.8.8.tgz", + "integrity": "sha512-RhIBtd5xhD/iLnxjt35jvOae28oNW/wtMZBOmQR3Rf0y4wirvA1bpAZEhBoFqL+rZGhsd6ddOdQXdex1T0DRyQ==", + "dev": true + }, + "cordova-plugin-device": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/cordova-plugin-device/-/cordova-plugin-device-2.0.3.tgz", + "integrity": "sha512-Jb3V72btxf3XHpkPQsGdyc8N6tVBYn1vsxSFj43fIz9vonJDUThYPCJJHqk6PX6N4dJw6I4FjxkpfCR4LDYMlw==", + "dev": true + }, + "cordova-plugin-foreground-service": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cordova-plugin-foreground-service/-/cordova-plugin-foreground-service-1.1.3.tgz", + "integrity": "sha512-/C2Z8w9JIfAaLeXP290msQP9jcHR+CAJYbp6/x+mS21VtvS2Ug0upKsgjEzzSzRnt08Mr8kuwCV7juUd6eCWcw==", + "dev": true + }, + "cordova-plugin-ionic-keyboard": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.2.0.tgz", + "integrity": "sha512-yDUG+9ieKVRitq5mGlNxjaZh/MgEhFFIgTIPhqSbUaQ8UuZbawy5mhJAVClqY97q8/rcQtL6dCDa7x2sEtCLcA==", + "dev": true + }, + "cordova-plugin-ionic-webview": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/cordova-plugin-ionic-webview/-/cordova-plugin-ionic-webview-4.2.1.tgz", + "integrity": "sha512-7KrmqLaOGq1RP8N2z1ezN1kqkWFzTwwMvQ3/qAkd+exxFZuOe3DIN4eaU1gdNphsxdirI8Ajnr9q4So5vQbWqw==", + "dev": true + }, + "cordova-plugin-local-notification": { + "version": "0.9.0-beta.2", + "resolved": "https://registry.npmjs.org/cordova-plugin-local-notification/-/cordova-plugin-local-notification-0.9.0-beta.2.tgz", + "integrity": "sha512-63n77K1pt8dnbWnNR8QWETi9Glezi1bvNHvHWmGNIOv0xCb0phZnm+Ku49BQ+omwe8Z5voMvrA4I03SYPpv38w==", + "dev": true + }, + "cordova-plugin-splashscreen": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-5.0.2.tgz", + "integrity": "sha1-dH509W4gHNWFvGLRS8oZ9oZ/8e0=", + "dev": true + }, + "cordova-plugin-statusbar": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/cordova-plugin-statusbar/-/cordova-plugin-statusbar-2.4.2.tgz", + "integrity": "sha1-/B+9wNjXAzp+jh8ff/FnrJvU+vY=", + "dev": true + }, + "cordova-plugin-whitelist": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.3.tgz", + "integrity": "sha1-tehezbv+Wu3tQKG/TuI3LmfZb7Q=", + "dev": true + }, "core-js": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", @@ -4490,6 +4895,12 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -4876,6 +5287,23 @@ "integrity": "sha512-GEQw+6dNWjueXGkGfjgm7dAMtXfEqrfDG3uWcZdeaD4cZ3dKYdPRQVruVXQRXtPLtOr5GNVVlNLRMChOZ611pQ==", "dev": true }, + "elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha1-mskb5uUvtuYkTE5UpKw+2K6OKcA=", + "dev": true, + "requires": { + "sax": "1.1.4" + }, + "dependencies": { + "sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha1-dLbTPJrh4AFRDxeakRaFiPGu2qk=", + "dev": true + } + } + }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -4948,6 +5376,17 @@ "once": "^1.4.0" } }, + "endent": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/endent/-/endent-1.4.1.tgz", + "integrity": "sha512-buHTb5c8AC9NshtP6dgmNLYkiT+olskbq1z6cEGvfGCF3Qphbu/1zz5Xu+yjTDln8RbxNhPoUyJ5H8MSrp1olQ==", + "dev": true, + "requires": { + "dedent": "^0.7.0", + "fast-json-parse": "^1.0.3", + "objectorarray": "^1.0.4" + } + }, "engine.io": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", @@ -5550,6 +5989,12 @@ "picomatch": "^2.2.1" } }, + "fast-json-parse": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", + "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -6531,6 +6976,12 @@ "debug": "4" } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -8894,6 +9345,12 @@ "has": "^1.0.3" } }, + "objectorarray": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz", + "integrity": "sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w==", + "dev": true + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -9042,12 +9499,28 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -9379,6 +9852,25 @@ "find-up": "^4.0.0" } }, + "plist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz", + "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==", + "dev": true, + "requires": { + "base64-js": "^1.2.3", + "xmlbuilder": "^9.0.7", + "xmldom": "0.1.x" + }, + "dependencies": { + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + } + } + }, "pnp-webpack-plugin": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", @@ -10804,6 +11296,15 @@ } } }, + "properties-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/properties-parser/-/properties-parser-0.3.1.tgz", + "integrity": "sha1-ExbpU5/7/ZOEXjabIRAiq9R4dxo=", + "dev": true, + "requires": { + "string.prototype.codepointat": "^0.2.0" + } + }, "protractor": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", @@ -11335,6 +11836,16 @@ } } }, + "read-chunk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", + "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "with-open-file": "^0.1.6" + } + }, "read-package-json-fast": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-1.2.2.tgz", @@ -12906,6 +13417,12 @@ "strip-ansi": "^6.0.0" } }, + "string.prototype.codepointat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==", + "dev": true + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -12952,12 +13469,24 @@ "ansi-regex": "^5.0.0" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "style-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", @@ -13534,6 +14063,12 @@ "integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==", "dev": true }, + "underscore": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", + "integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -15053,6 +15588,17 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, + "with-open-file": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", + "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", + "dev": true, + "requires": { + "p-finally": "^1.0.0", + "p-try": "^2.1.0", + "pify": "^4.0.1" + } + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", @@ -15175,6 +15721,12 @@ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true }, + "xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", + "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", + "dev": true + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", diff --git a/package.json b/package.json index 1d20ded..22176bd 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,11 @@ "@angular/platform-browser": "~11.2.0", "@angular/platform-browser-dynamic": "~11.2.0", "@angular/router": "~11.2.0", + "@capacitor/core": "^2.4.6", + "@ionic-native/background-mode": "^5.31.1", + "@ionic-native/core": "^5.31.1", + "@ionic-native/foreground-service": "^5.31.1", + "@ionic-native/local-notifications": "^5.31.1", "@ionic/angular": "^5.5.2", "rxjs": "~6.6.0", "tslib": "^2.0.0", @@ -35,6 +40,17 @@ "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", "codelyzer": "^6.0.0", + "cordova-android": "^9.0.0", + "cordova-plugin-background-mode": "^0.7.3", + "cordova-plugin-badge": "^0.8.8", + "cordova-plugin-device": "^2.0.3", + "cordova-plugin-foreground-service": "^1.1.3", + "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-splashscreen": "^5.0.2", + "cordova-plugin-statusbar": "^2.4.2", + "cordova-plugin-whitelist": "^1.3.3", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~5.2.0", @@ -48,5 +64,23 @@ "tslint": "~6.1.0", "typescript": "~4.0.2" }, - "description": "An Ionic project" + "description": "An Ionic project", + "cordova": { + "plugins": { + "cordova-plugin-local-notification": {}, + "cordova-plugin-whitelist": {}, + "cordova-plugin-statusbar": {}, + "cordova-plugin-device": {}, + "cordova-plugin-splashscreen": {}, + "cordova-plugin-ionic-webview": { + "ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+" + }, + "cordova-plugin-ionic-keyboard": {}, + "cordova-plugin-background-mode": {}, + "cordova-plugin-foreground-service": {} + }, + "platforms": [ + "android" + ] + } } diff --git a/resources/android/icon/drawable-hdpi-icon.png b/resources/android/icon/drawable-hdpi-icon.png new file mode 100644 index 0000000..0913286 Binary files /dev/null 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 new file mode 100644 index 0000000..9aa7982 Binary files /dev/null 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 new file mode 100644 index 0000000..f630706 Binary files /dev/null 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 new file mode 100644 index 0000000..717b017 Binary files /dev/null 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 new file mode 100644 index 0000000..cb7d456 Binary files /dev/null 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 new file mode 100644 index 0000000..53d8b39 Binary files /dev/null and b/resources/android/icon/drawable-xxxhdpi-icon.png differ diff --git a/resources/android/splash/drawable-land-hdpi-screen.png b/resources/android/splash/drawable-land-hdpi-screen.png new file mode 100644 index 0000000..1932966 Binary files /dev/null and b/resources/android/splash/drawable-land-hdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-ldpi-screen.png b/resources/android/splash/drawable-land-ldpi-screen.png new file mode 100644 index 0000000..b3b0cc0 Binary files /dev/null and b/resources/android/splash/drawable-land-ldpi-screen.png differ diff --git a/resources/android/splash/drawable-land-mdpi-screen.png b/resources/android/splash/drawable-land-mdpi-screen.png new file mode 100644 index 0000000..e00f74b Binary files /dev/null and b/resources/android/splash/drawable-land-mdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-xhdpi-screen.png b/resources/android/splash/drawable-land-xhdpi-screen.png new file mode 100644 index 0000000..533d3a7 Binary files /dev/null and b/resources/android/splash/drawable-land-xhdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-xxhdpi-screen.png b/resources/android/splash/drawable-land-xxhdpi-screen.png new file mode 100644 index 0000000..f64e154 Binary files /dev/null and b/resources/android/splash/drawable-land-xxhdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-xxxhdpi-screen.png b/resources/android/splash/drawable-land-xxxhdpi-screen.png new file mode 100644 index 0000000..47844f8 Binary files /dev/null and b/resources/android/splash/drawable-land-xxxhdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-hdpi-screen.png b/resources/android/splash/drawable-port-hdpi-screen.png new file mode 100644 index 0000000..0089232 Binary files /dev/null and b/resources/android/splash/drawable-port-hdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-ldpi-screen.png b/resources/android/splash/drawable-port-ldpi-screen.png new file mode 100644 index 0000000..76e8c70 Binary files /dev/null and b/resources/android/splash/drawable-port-ldpi-screen.png differ diff --git a/resources/android/splash/drawable-port-mdpi-screen.png b/resources/android/splash/drawable-port-mdpi-screen.png new file mode 100644 index 0000000..9785306 Binary files /dev/null and b/resources/android/splash/drawable-port-mdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-xhdpi-screen.png b/resources/android/splash/drawable-port-xhdpi-screen.png new file mode 100644 index 0000000..b4f1315 Binary files /dev/null and b/resources/android/splash/drawable-port-xhdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-xxhdpi-screen.png b/resources/android/splash/drawable-port-xxhdpi-screen.png new file mode 100644 index 0000000..83af947 Binary files /dev/null and b/resources/android/splash/drawable-port-xxhdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-xxxhdpi-screen.png b/resources/android/splash/drawable-port-xxxhdpi-screen.png new file mode 100644 index 0000000..76e01cc Binary files /dev/null and b/resources/android/splash/drawable-port-xxxhdpi-screen.png differ diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 0000000..0dfc0ca Binary files /dev/null and b/resources/icon.png differ diff --git a/src/app/api.service.ts b/src/app/api.service.ts new file mode 100644 index 0000000..7e6cee8 --- /dev/null +++ b/src/app/api.service.ts @@ -0,0 +1,52 @@ +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {HttpClient} from '@angular/common/http'; +import {Token} from './token'; +import {Host} from './host'; +import {ChatMessage} from './chat.message'; +import {ChatTokenResponse} from './chat.token'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + public static userToken: string; + + 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(Host.URL + '/token', {username, password}); + } + + getChatToken(authToken: string): Observable { + return this.client.get( + Host.URL + '/session/chat', + {headers: {Authorization: 'Bearer ' + authToken}} + ); + } + + getChatHistory(token: string, offset: number, limit: number): Observable { + return this.client.get( + Host.URL + '/session/chat/history?limit=' + limit + '&offset=' + offset, + {headers: {Authorization: 'Bearer ' + token}} + ); + } + + deleteAuthToken(token: string): Observable + { + return this.client.delete( + Host.URL + '/token/' + token, + {headers: {Authorization: 'Bearer ' + token}} + ); + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e7b0d5a..75b1f5d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -2,15 +2,6 @@ import { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; const routes: Routes = [ - { - path: 'home', - loadChildren: () => import('./home/home.module').then( m => m.HomePageModule) - }, - { - path: '', - redirectTo: 'home', - pathMatch: 'full' - }, ]; @NgModule({ diff --git a/src/app/app.component.html b/src/app/app.component.html index 13b9677..0b117df 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,4 @@ - + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 913de3d..01abf81 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,23 @@ import { Component } from '@angular/core'; +import { ApiService } from './api.service'; @Component({ selector: 'app-root', - templateUrl: 'app.component.html', - styleUrls: ['app.component.scss'], + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] }) export class AppComponent { - constructor() {} + public static token: string = null; + + title = 'METAsocket'; + + public constructor(private apiService: ApiService) + { + AppComponent.token = this.apiService.getFromStorage('token'); + } + + public getToken(): string + { + return AppComponent.token; + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f8b75ac..be80f6c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,14 +4,25 @@ 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'; +import {LocalNotifications} from '@ionic-native/local-notifications/ngx'; +import {BackgroundMode} from '@ionic-native/background-mode/ngx'; +import {ForegroundService} from '@ionic-native/foreground-service/ngx'; @NgModule({ - declarations: [AppComponent], + declarations: [AppComponent, ChatComponent, LoginComponent, TopbarComponent], entryComponents: [], - imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule], - providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }], + imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, FormsModule, HttpClientModule], + providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, LocalNotifications, BackgroundMode, ForegroundService + ], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/src/app/chat.message.ts b/src/app/chat.message.ts new file mode 100644 index 0000000..69b362c --- /dev/null +++ b/src/app/chat.message.ts @@ -0,0 +1,7 @@ +export interface ChatMessage +{ + userId: number; + username: string; + message: string; + datetime: string; +} diff --git a/src/app/chat.token.ts b/src/app/chat.token.ts new file mode 100644 index 0000000..2062282 --- /dev/null +++ b/src/app/chat.token.ts @@ -0,0 +1,5 @@ +export interface ChatTokenResponse +{ + userId: number; + token: string; +} diff --git a/src/app/chat/chat.component.html b/src/app/chat/chat.component.html new file mode 100644 index 0000000..6ea002a --- /dev/null +++ b/src/app/chat/chat.component.html @@ -0,0 +1,17 @@ +
+ +
+
+ +
+
{{message.username}}
+
{{message.message}}
+
{{message.datetime}}
+
+
+
+ +
+ +
+
diff --git a/src/app/chat/chat.component.scss b/src/app/chat/chat.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/chat/chat.component.spec.ts b/src/app/chat/chat.component.spec.ts new file mode 100644 index 0000000..89a60fa --- /dev/null +++ b/src/app/chat/chat.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ChatComponent } from './chat.component'; + +describe('ChatComponent', () => { + let component: ChatComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ ChatComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(ChatComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat/chat.component.ts b/src/app/chat/chat.component.ts new file mode 100644 index 0000000..f3ee026 --- /dev/null +++ b/src/app/chat/chat.component.ts @@ -0,0 +1,162 @@ +import {Plugins, AppState} from '@capacitor/core'; +import {AfterViewChecked, AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {ChatMessage} from '../chat.message'; +import {Host} from '../host'; +import {ApiService} from '../api.service'; +import {WebsocketListener} from '../websocket.listener'; +import {WebsocketService} from '../websocket.service'; +import {LocalNotifications} from '@ionic-native/local-notifications/ngx'; +import {BackgroundMode} from '@ionic-native/background-mode/ngx'; +import {ForegroundService} from '@ionic-native/foreground-service/ngx'; + +const {App} = Plugins; + +@Component({ + selector: 'app-chat', + templateUrl: './chat.component.html', + styleUrls: ['./chat.component.scss'], +}) +export class ChatComponent implements OnInit, AfterViewInit, AfterViewChecked, WebsocketListener { + messages: ChatMessage[] = []; + userToken: string; + userId: number; + url: string; + + @ViewChild('chatPostArea') chatPostArea: ElementRef; + chatText: string; + + private oldScrollHeight = 0; + private messageOffset = 0; + private messageLimit = 10; + private hasBeenReloaded = false; + private hasFocus = true; + + public constructor( + private apiService: ApiService, + private websocketService: WebsocketService, + private localNotifications: LocalNotifications, + private backgroundMode: BackgroundMode, + private foregroundService: ForegroundService + ) { + this.userToken = this.apiService.getFromStorage('token'); + this.userId = Number(this.apiService.getFromStorage('userId')); + this.url = Host.URL; + this.websocketService.setListener(this); + this.websocketService.initializeSocket(this.apiService.getFromStorage('chatToken')); + this.backgroundMode.disableBatteryOptimizations(); + this.backgroundMode.disableWebViewOptimizations(); + } + + ngAfterViewInit(): void { + this.chatPostArea.nativeElement.scroll(0, this.chatPostArea.nativeElement.scrollHeight); + } + + ngAfterViewChecked(): void { + if (this.oldScrollHeight !== this.chatPostArea.nativeElement.scrollHeight) { + const scrollTop = this.chatPostArea.nativeElement.scrollTop; + const clientHeight = this.chatPostArea.nativeElement.clientHeight; + + if (this.hasBeenReloaded) { + this.chatPostArea.nativeElement.scroll(0, this.chatPostArea.nativeElement.scrollHeight - this.oldScrollHeight); + this.hasBeenReloaded = false; + } else if (scrollTop + clientHeight > this.oldScrollHeight - 10) { + this.chatPostArea.nativeElement.scroll(0, this.oldScrollHeight); + } + + this.oldScrollHeight = this.chatPostArea.nativeElement.scrollHeight; + } + } + + ngOnInit(): void { + if (this.userToken === null) { + return; + } + + this.localNotifications.requestPermission(); + + this.foregroundService.start('METAsocket', 'The chat for WowApp', 'ic_stat_notification_icon_enabled'); + + this.apiService.getChatHistory(this.userToken, this.messageOffset, this.messageLimit).subscribe( + (response) => { + this.messages = response; + this.messageOffset += this.messageLimit; + } + ); + + App.addListener('appStateChange', (state: AppState) => { + this.hasFocus = state.isActive; + }); + + setInterval( + () => { + this.websocketService.sendKeepAliveMessage(); + }, 1000 * 60 // every minute + ); + } + + onScroll(): void { + if (this.chatPostArea.nativeElement.scrollTop === 0) { + this.apiService.getChatHistory(this.userToken, this.messageOffset, this.messageLimit).subscribe( + (response) => { + this.messages = response.concat(this.messages); + this.messageOffset += this.messageLimit; + this.hasBeenReloaded = true; + } + ); + } + } + + onTextInput(event: Event): void { + if (!(event instanceof KeyboardEvent)) { + return; + } + + switch (event.key) { + case 'Enter': + event.preventDefault(); + + if (this.chatText.trim() === '') { + return; + } + + this.websocketService.sendChatMessage(this.chatText); + this.chatText = ''; + + return; + + default: + return; + } + } + + onChatMessage(message: ChatMessage): void { + this.messages.push(message); + this.messageOffset++; + + if (message.userId === this.userId) { + return; + } + + this.triggerNotification(message); + } + + triggerNotification(message: ChatMessage): void + { + this.localNotifications.schedule( + { + title: message.username, + text: message.message, + id: 1, + priority: 2, + lockscreen: true, + autoClear: true, + icon: Host.URL + '/user/' + message.userId + '/avatar?token=' + this.userToken, + smallIcon: 'ic_stat_notification_icon_enabled', + led: {color: '#ff00ff', on: 500, off: 500}, + trigger: { at: new Date(new Date().getTime() + 1000) }, + sound: 'file://assets/audio/murloc.wav', + vibrate: true + } + ); + } +} diff --git a/src/app/host.ts b/src/app/host.ts new file mode 100644 index 0000000..e0bb978 --- /dev/null +++ b/src/app/host.ts @@ -0,0 +1,5 @@ +export class Host +{ + public static readonly URL: string = 'https://sabolli.de/wow/api/v1'; + public static readonly WEBSOCKET: string = 'wss://sabolli.de/metasocket'; +} diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html new file mode 100644 index 0000000..9b2927b --- /dev/null +++ b/src/app/login/login.component.html @@ -0,0 +1,20 @@ +
+
+ +

{{error}}

+ +
+ + + + + +
+
+
diff --git a/src/app/login/login.component.scss b/src/app/login/login.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..b9cb059 --- /dev/null +++ b/src/app/login/login.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ LoginComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts new file mode 100644 index 0000000..fdb818e --- /dev/null +++ b/src/app/login/login.component.ts @@ -0,0 +1,71 @@ +import { Component, OnInit } from '@angular/core'; +import { ApiService } from '../api.service'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'] +}) +export class LoginComponent implements OnInit { + username = ''; + password = ''; + error: string = null; + + constructor(private apiService: ApiService) { } + + ngOnInit(): void {} + + login(event): void + { + event.target.disabled = true; + event.target.style.visibility = 'hidden'; + + this.apiService.getAuthToken(this.username, this.password).toPromise() + .then( + (response) => { + this.apiService.storeData('token', response.token); + + this.apiService.getChatToken(response.token).toPromise() + .then( + (chatTokenResponse) => { + this.apiService.storeData('chatToken', chatTokenResponse.token); + this.apiService.storeData('userId', chatTokenResponse.userId.toString()); + + window.location.reload(); + } + ).catch( + (error) => { + this.apiService.storeData('token', null); + this.error = error.name + ': ' + error.message; + + event.target.disabled = false; + event.target.style.visibility = 'visible'; + } + ); + } + ).catch ( + (error) => { + this.apiService.storeData('token', null); + + switch (error.status) { + case 401: + this.error = 'Login fehlgeschlagen: Die Zugangsdaten sind falsch!'; + break; + + case 0: + this.error = 'Verbindungsfehler: Konnte keine Verbindung zum Server herstellen!'; + break; + + default: + this.error = 'Fehler: Etwas unfassbar schlimmes ist passiert!'; + break; + } + + console.log(error); + + event.target.disabled = false; + event.target.style.visibility = 'visible'; + } + ); + } +} diff --git a/src/app/socket.keepalive.message.ts b/src/app/socket.keepalive.message.ts new file mode 100644 index 0000000..b7545a1 --- /dev/null +++ b/src/app/socket.keepalive.message.ts @@ -0,0 +1,4 @@ +export interface SocketKeepaliveMessage +{ + type: number; +} diff --git a/src/app/socket.received.message.ts b/src/app/socket.received.message.ts new file mode 100644 index 0000000..a33bc81 --- /dev/null +++ b/src/app/socket.received.message.ts @@ -0,0 +1,6 @@ +export interface SocketReceivedMessage { + type: number; + userId: number; + message: string; + datetime: string; +} diff --git a/src/app/socket.registration.message.ts b/src/app/socket.registration.message.ts new file mode 100644 index 0000000..11a7ea8 --- /dev/null +++ b/src/app/socket.registration.message.ts @@ -0,0 +1,5 @@ +export interface SocketRegistrationMessage { + type: number; + token: string; +} + diff --git a/src/app/socket.send.message.ts b/src/app/socket.send.message.ts new file mode 100644 index 0000000..cbe6572 --- /dev/null +++ b/src/app/socket.send.message.ts @@ -0,0 +1,4 @@ +export interface SocketSendMessage { + type: number; + message: string; +} diff --git a/src/app/token.ts b/src/app/token.ts new file mode 100644 index 0000000..8b6cc8e --- /dev/null +++ b/src/app/token.ts @@ -0,0 +1,3 @@ +export interface Token { + token: string; +} \ No newline at end of file diff --git a/src/app/topbar/topbar.component.html b/src/app/topbar/topbar.component.html new file mode 100644 index 0000000..c4a8244 --- /dev/null +++ b/src/app/topbar/topbar.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/src/app/topbar/topbar.component.scss b/src/app/topbar/topbar.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/topbar/topbar.component.spec.ts b/src/app/topbar/topbar.component.spec.ts new file mode 100644 index 0000000..deb90d0 --- /dev/null +++ b/src/app/topbar/topbar.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { TopbarComponent } from './topbar.component'; + +describe('TopbarComponent', () => { + let component: TopbarComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ TopbarComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(TopbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/topbar/topbar.component.ts b/src/app/topbar/topbar.component.ts new file mode 100644 index 0000000..35173b5 --- /dev/null +++ b/src/app/topbar/topbar.component.ts @@ -0,0 +1,31 @@ +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'; + +@Component({ + selector: 'app-topbar', + templateUrl: './topbar.component.html', + styleUrls: ['./topbar.component.scss'] +}) +export class TopbarComponent implements OnInit { + @Input() token: string; + + constructor(private apiService: ApiService, private foregroundService: ForegroundService) { + } + + ngOnInit(): void { + } + + logout(): void { + this.apiService.deleteAuthToken(this.apiService.getFromStorage('token')).toPromise() + .then() + .catch( + (error) => {console.log(error); } + ); + this.apiService.storeData('token', null); + this.foregroundService.stop(); + + AppComponent.token = null; + } +} diff --git a/src/app/websocket.listener.ts b/src/app/websocket.listener.ts new file mode 100644 index 0000000..f318a2b --- /dev/null +++ b/src/app/websocket.listener.ts @@ -0,0 +1,6 @@ +import {ChatMessage} from './chat.message'; + +export interface WebsocketListener +{ + onChatMessage(message: ChatMessage): void; +} diff --git a/src/app/websocket.service.ts b/src/app/websocket.service.ts new file mode 100644 index 0000000..927ea46 --- /dev/null +++ b/src/app/websocket.service.ts @@ -0,0 +1,104 @@ +import {Injectable} from '@angular/core'; +import {Host} from './host'; +import {ChatMessage} from './chat.message'; +import {SocketRegistrationMessage} from './socket.registration.message'; +import {SocketReceivedMessage} from './socket.received.message'; +import {WebsocketListener} from './websocket.listener'; +import {SocketSendMessage} from './socket.send.message'; +import {SocketKeepaliveMessage} from './socket.keepalive.message'; + +@Injectable({ + providedIn: 'root' +}) +export class WebsocketService { + private socket: WebSocket = new WebSocket(Host.WEBSOCKET); + private userList: Map = new Map(); + private listener: WebsocketListener; + private chatToken: string; + + initializeSocket(chatToken: string): void { + this.chatToken = chatToken; + + this.socket = new WebSocket(Host.WEBSOCKET); + this.socket.addEventListener('open', () => { + this.authorize(); + }); + this.socket.addEventListener('message', (transmission: MessageEvent) => { + this.handleIncomingTransmission(transmission); + }); + this.socket.addEventListener( + 'close', + () => { + this.initializeSocket(this.chatToken); + this.authorize(); + } + ); + } + + setListener(listener: WebsocketListener): void { + this.listener = listener; + } + + sendChatMessage(message: string): void { + const socketMessage: SocketSendMessage = { + type: Response.CHAT_MESSAGE, + message + }; + this.socket.send(JSON.stringify(socketMessage)); + } + + sendKeepAliveMessage(): void { + const socketMessage: SocketKeepaliveMessage = { + type: Response.KEEP_ALIVE + }; + + this.socket.send(JSON.stringify(socketMessage)); + } + + private authorize(): void { + const message: SocketRegistrationMessage = {type: Response.REGISTRATION_MESSAGE, token: this.chatToken}; + this.socket.send(JSON.stringify(message)); + } + + private handleIncomingTransmission(transmission: MessageEvent): void { + const response = JSON.parse(transmission.data); + + switch (response.type) { + case Response.CHAT_MESSAGE: + const messageReceived: SocketReceivedMessage = response; + + const message: ChatMessage = { + userId: messageReceived.userId, + username: this.userList.get(messageReceived.userId), + datetime: messageReceived.datetime, + message: messageReceived.message + }; + + this.listener.onChatMessage(message); + + break; + + case Response.REGISTRATION_MESSAGE: + this.userList.set(response.userId, response.username); + break; + + case Response.USERLIST: + response.users.forEach( + (user) => { + this.userList.set(user.userId, user.username); + } + ); + break; + + default: + throw new Error('Unknown message type: ' + response.type); + } + } +} + +enum Response { + CHAT_MESSAGE = 1, + REGISTRATION_MESSAGE, + USERLIST, + KEEP_ALIVE +} diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/audio/murloc.ogg b/src/assets/audio/murloc.ogg new file mode 100644 index 0000000..3c07d60 Binary files /dev/null and b/src/assets/audio/murloc.ogg differ diff --git a/src/assets/audio/murloc.wav b/src/assets/audio/murloc.wav new file mode 100644 index 0000000..bfc2395 Binary files /dev/null and b/src/assets/audio/murloc.wav differ diff --git a/src/assets/graphics/bg_responsive.jpg b/src/assets/graphics/bg_responsive.jpg new file mode 100644 index 0000000..51d9314 Binary files /dev/null and b/src/assets/graphics/bg_responsive.jpg differ diff --git a/src/assets/graphics/metasocket-banner.svg b/src/assets/graphics/metasocket-banner.svg new file mode 100644 index 0000000..ec9682f --- /dev/null +++ b/src/assets/graphics/metasocket-banner.svg @@ -0,0 +1,83 @@ + + + + + + + + image/svg+xml + + + + + + + META + socket + diff --git a/src/assets/graphics/metasocket-icon.svg b/src/assets/graphics/metasocket-icon.svg new file mode 100644 index 0000000..721f05d --- /dev/null +++ b/src/assets/graphics/metasocket-icon.svg @@ -0,0 +1,61 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/src/assets/icon/favicon.png b/src/assets/icon/favicon.png index 51888a7..fdc08a8 100644 Binary files a/src/assets/icon/favicon.png and b/src/assets/icon/favicon.png differ diff --git a/src/global.scss b/src/global.scss index d854de8..3116d92 100644 --- a/src/global.scss +++ b/src/global.scss @@ -1,26 +1,175 @@ -/* - * App Global CSS - * ---------------------------------------------------------------------------- - * Put style rules here that you want to apply globally. These styles are for - * the entire app and not just one component. Additionally, this file can be - * used as an entry point to import other CSS/Sass files to be included in the - * output CSS. - * For more information on global stylesheets, visit the documentation: - * https://ionicframework.com/docs/layout/global-stylesheets - */ +* { + box-sizing: border-box; + font-family: sans-serif; + outline: none; +} -/* Core CSS required for Ionic components to work properly */ -@import "~@ionic/angular/css/core.css"; +#chat { + background: rgb(27, 47, 114) url("assets/graphics/bg_responsive.jpg") repeat; + margin: 0; + font-size: 16px; + font-family: sans-serif; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + color: white; +} -/* Basic CSS for apps built with Ionic */ -@import "~@ionic/angular/css/normalize.css"; -@import "~@ionic/angular/css/structure.css"; -@import "~@ionic/angular/css/typography.css"; -@import '~@ionic/angular/css/display.css'; +#chat-post-area { + position: fixed; + top: 40px; + left: 0; + right: 0; + bottom: 50px; + overflow-x: hidden; + box-sizing: border-box; + padding-bottom: 20px; +} + +#chat-type-area { + position: fixed; + height: 50px; + left: 0; + right: 0; + bottom: 0; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.9); +} + +#chat-textarea { + width: 100%; + height: 100%; + background-color: #333333; + font-family: sans-serif; + font-size: 16px; + color: white; + border: 2px grey solid; + box-sizing: border-box; + padding: 5px; + resize: none; + outline: none; +} + +.chat-post { + background-color: rgba(37, 26, 37, 0.9); + border-radius: 0 20px 20px 0; + margin-right: 20px; + margin-left: 0; + margin-top: 10px; + margin-bottom: 10px; + padding: 20px; + overflow: hidden; +} + +.chat-own-post { + border-radius: 20px 0 0 20px; + margin-left: 20px; + margin-right: 0; + padding: 20px 0 20px 20px; + background-color: rgba(26, 37, 26, 0.9); +} + +.chat-avatar { + width: 64px; + height: 64px; + border-radius: 32px; + margin-right: 20px; + float: left; +} + +.chat-own-post > .chat-avatar { + margin-left: 20px; + float: right; +} + +.chat-username { + font-weight: bold; +} + +.chat-post-message { + margin-top: 10px; + overflow: hidden; +} + +.chat-datetime { + font-style: italic; + color: grey; + margin-top: 10px; +} + +#login { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgb(37, 26, 37); + display: flex; + align-items: center; +} + +#login-form { + max-width: 500px; + margin: auto; + text-align: center; + padding: 20px; +} + +#login-form label { + display: block; + color: white; + font-weight: bold; + margin-bottom: 20px; +} + +#login-form input { + display: block; + background-color: transparent; + border: solid 1px white; + color: white; + border-radius: 5px; + font-size: 18px; + width: 100%; + padding: 5px; +} + +#login-form input[type=submit] { + margin-top: 50px; +} + +.error-message { + background-color: #550000; + border: solid 1px red; + color: white; + padding: 5px; + margin-bottom: 30px; +} + +#metasocket-banner { + width: 100%; + margin-bottom: 50px; +} + +#metasocket-logo { + height: 100%; +} + +#topbar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 40px; + background-color: #888888; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.9); + padding: 5px; + display: inline-flex; + justify-content: space-between; +} + +#logout-button { + width: 70px; + text-align: center; +} -/* Optional CSS utils that can be commented out */ -@import "~@ionic/angular/css/padding.css"; -@import "~@ionic/angular/css/float-elements.css"; -@import "~@ionic/angular/css/text-alignment.css"; -@import "~@ionic/angular/css/text-transformation.css"; -@import "~@ionic/angular/css/flex-utils.css";