From 00a9a8c67f477d5654ee8b18cd21bb3c17662667 Mon Sep 17 00:00:00 2001 From: Mal <=> Date: Sun, 21 Mar 2021 23:56:54 +0100 Subject: [PATCH] Version 1.5.0: Update check and dialog popups implemented --- Makefile | 2 + build/android/ic_launcher.svg | 23 +++-- build/android/ic_launcher_round.svg | 67 ------------- config.xml | 2 +- package-lock.json | 93 ++++-------------- package.json | 1 + resources/android/icon/drawable-hdpi-icon.png | Bin 1694 -> 3057 bytes resources/android/icon/drawable-ldpi-icon.png | Bin 1060 -> 1669 bytes resources/android/icon/drawable-mdpi-icon.png | Bin 1319 -> 2191 bytes .../android/icon/drawable-xhdpi-icon.png | Bin 2028 -> 3819 bytes .../android/icon/drawable-xxhdpi-icon.png | Bin 2785 -> 5516 bytes .../android/icon/drawable-xxxhdpi-icon.png | Bin 3594 -> 7425 bytes src/app/api.service.ts | 15 ++- src/app/app.component.ts | 3 +- src/app/app.config.ts | 4 + src/app/app.module.ts | 19 ++-- src/app/app.storage.ts | 77 +++++++++++++++ src/app/chat/chat.component.ts | 72 ++++++++++++-- src/app/fullscreen.dialog.ts | 73 ++++++++++++++ src/app/fullscreen.error.ts | 9 ++ src/app/fullscreen.notification.ts | 10 ++ src/app/login/login.component.html | 6 +- src/app/login/login.component.ts | 31 ++++-- src/app/topbar/topbar.component.ts | 9 +- src/app/version.info.ts | 5 + src/global.scss | 75 ++++++++++++++ 26 files changed, 396 insertions(+), 200 deletions(-) delete mode 100644 build/android/ic_launcher_round.svg create mode 100644 src/app/app.config.ts create mode 100644 src/app/app.storage.ts create mode 100644 src/app/fullscreen.dialog.ts create mode 100644 src/app/fullscreen.error.ts create mode 100644 src/app/fullscreen.notification.ts create mode 100644 src/app/version.info.ts 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 091328638e895a70a5c8e1db00ab161929af9adf..e97cc48ff75f5133310d7cb631e35450f667ea0a 100644 GIT binary patch delta 3008 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 diff --git a/resources/android/icon/drawable-ldpi-icon.png b/resources/android/icon/drawable-ldpi-icon.png index 9aa79825eca379f4193d9397499b8fbc18525871..2b5a9f2f91b708390f0476434f91798bf6628255 100644 GIT binary patch delta 1609 zcmV-P2DbU62!#!hEDGQN00H0u1?rzkkv1oP1`phEmN~bpml?m z%0gJJN_p#B3N39K+KDK226=H3sw%W1Z_>8L_SuKT7hlKNF*N&2SMojQf6n>c$M@cU z=Q&r%Qkz|yi?oRL2*S@5s#uUhz&77KrBEY+gha{tSS)rrI2s(tW`0?WsfHTeUTFU% zLNoBQ>=uwuQ2a#;Yi|Y!MDG~Lx5ifoyacSx1|)_0m8jnYhk|Er3#j4#hUL~})|Zr0 zJMIv~EJddt)t?WH1^&HgAfLWL@w_l)Up5)pRzcToRyJxVD6-uc@*i-y_#yWNNgB0|TD9jKT}oO}HwlYa((n&NF* z{70jsu@=RFWSCuTyLj~JM~(A}jTL+w{+4hkOxcDqipq*uy>_*6$iy|&H-!I5Khrm+0hs!JiVgJ}uvq49#b_kTA^#!18Ty9V|ITvp)J4jx%dxMp z8*{`X)$>75;?(!W@?&3zCPKYBfGBBBj^o(C7)mK41tt!4M`cQgF;@B)}@ zbWiR*_>cH;x}6-}clcHhg^H+%OMHdCW`u*O901-%FJFH2EjqLC;g{Ucc~Fw#FlG?KAUjBCmQjHYwUOwKTWaCw0CrgnNS_GS&# zd$E`HrgjD{4=^)1linU+bvT^jM!-{G!?p0 zl3(%&N$;cjXX@F1wQm<9LjH<;Jk=gPefLv-_|FffSxO%$rNUc*tIWl+<;%E!&0Hqi z>^9ntw&8R;je2TkVumaKOiG`M3&C*kPw{MjP>C|_w^q4U8JQlyQR={d#BWNnRhz14 z>1Z+DGto$t_NI2a-|IGD5;Hx_)micCz{F!wFT;FIt%bFJH-Y{{LuYFzC;xuZG{{lv zpzUZIx%ONDe9!qzFFj4y@IB`P<}&==*Y=uV0L~seOJ{3mGEbj%$a);+E5-nUs84)* zP#Bvyr4)z!hp>pnR5cbB7gD~loUULOZyb07TfX^*pPrbDUgg7;INeTD->H9`qO-Nr zsLgMNhduFsU9E9>p^4C7NnXiHgvvzkr4KJ*v)fqfS&LGN!}|_%?$|j1#z)5)y)tS# z#z)72xl(uH`UI5^S0W;uJ$9B?fBWhJ7(re+5k6s*Kc-!W9@U?p)n+S1WPPegT^qc% z=p6^Znb*%?z01l<=gO3k5#+O6a$hjFC2331mKBkINaSOdexn$Q+!d*Q`(JN=m)N)< zxdyHVuIsv94RkLRi6Ecpy6#PeNeN^?Mf8opUzY&pm0WUbQtj*K`ZMTp=i|SK#bPf5 z#hJVr)Q4iviSUW{)0r~XL&pb?pVNnRw?b`0xB#e3V-loLoEPL*o?%Z}CKy=c7qmn* z6gD_CtYWQIqN-7p05?DBDW7{Kmcy9o0$hQYlc~owc1KkOA~h|@9O#)0)1#1HbzII zgGe-jtHZVCAg&G0u8`)}Ogsw^uco+L^Rka&{NG*2TL&NA67v+a{@Ts(J?se4l;1Eo`G zo+X|kSIOMJcc09He*4&XpS|1b##(J%cOYxE6}Q)I=%+jFJqJAugx}A9=!irk*y?Rm zeq5=0pDI{G-fk}g;Q>TZM6Qx)`?8JLofvQae53JmwmUh{d4R^|Mk+q4Ae~BU?AB^4 zSMOY<_S4$@ed<-_6|;|L-w9MOLuQLPKj{H#>S`FfHKxgwtHVWTG?ec%%Cf@AZ%z`s z9m~V4p7ikcl4rJ<3udT)U=59gM+lz{Yt~kvR%)s%(4}d5l1ZW~&;|T`Osio4hR+W( z5*{fCq+3JdL*tZ}muqf~J8V1hQ~Sc13slOLnmw=ocn!eb*1g#3Z5rF9;3XnIMD#)$ z_o4X1I7>@Q>}%VHs;UG}1-W_cCIH!mEc3t3Yo6>v7J%ezlFaW;8TNm%A3+cpK0j;- z14aIIj1P@dR#rxCF~{WiWHDO+h+c~#nIy~>v+gmi*2Zg$A6lD0M%n~2(qFO3N>646 RYe)b9002ovPDHLkV1lYh=%)Yx diff --git a/resources/android/icon/drawable-mdpi-icon.png b/resources/android/icon/drawable-mdpi-icon.png index f63070620cc3ff9b41bae5cc680c37acfdc43cd7..f04d49d7e8a9d8ea6d64a09f16e1fb91336f175b 100644 GIT binary patch delta 2135 zcmV-d2&ng`3Xc(xEDC@C00MvkTTNeZkv1oP2pma7K~!jg&6;~mlvf(YfA2Rlb_8h$ zl!A^GM3jq7UAu}XiB)Si-f9?>_14{J?7D7ijhkq9TQ99>byIA5!6sUDx7jM%B>^wl zwR`cJv|a!uF%5&QMHCgVjvylFGIRD1M1lFfnPEV?&wq2y^Pcy4=X~Ee=Y7v7bOSPf zV>6@O$~{4e8U-YZk~k%*H_+=o<+dofp+x-vI2G!o1dPH$X@*PQ67)g9pzxOS5HDUS-~u z&e;ZtML%C;sku?}nkZS1Ja{y^8+QbM$!;_^Zanz?!MlMN1QfvZfzuOA%Jd~LtV{9& zM+q9U3VtX!?~kbYBc#Wq&oFsRMLa(706)V+QASLL!ynP)t1~?|eZC-nMX+|Cwt(>j z!ptOFQjN3LS!6^q3?L&mI9gBZ3#wW720R)Tc&&quTn| zShr^#1BMLfvO;^DoppQG5fL3>$Wcmtm>!#+uGi-^1JeghkJps;51^->fvt~?wYh7F ze=eTUQ$}<4=vnG+)CDa*Yj+j{5(Y4G;z&;Z^CXS88}+D~D48}id}u*wZRwAO0b~x% z?7?0B4aE2o|IFZL0I-HwgASs9Rebo<;Q)l$!h8oI^zh(G8#`$1pNp>*dmc8ofg{%O zM_{9#!-%K|vUX?DZ(u*KK3BDiB@>p=bf<~YlSdOB6Rj({s-lV$2Tx!Pv9kH_W+I}z zADgS?S6MS>4X$dJe&oW6g7Sh*UIAqG&$PSE?lOf6?P$~SpKl9~+e6NOiX4s=90TC3 z?Qb#i#gV$c3l9~t@zs|9Cv9pP%RX9$$?Uz3T)TLU)pJ(ks?rHUaa)if`4#!spv_C% zY;InmCq5!7g7tq}Zz%qg<)1td4{-jh@!r$6@9V#RP0orOJZ_H{uWxK$vU0O@X9%H} z($wiqGHsoWlPUqM5V@{BKWvtIKU2W9g=)5BLDJKi0D4?Jc^0HVPGrnc2XnCV<4J z5;u#8_uC2Fa^0fnP!XwN=Ywb7NPH`5Dr=~|Tut@mYHBKLd~z$C6$O}yf7u^@UAe{GS2AKU<|sq0F+$%i1bwfkvcKzP*RJL|)P zC)Jc>OZq)9C=i1Y6Gkw1`COs~MByxQ2CGpQj>Qh9Ettl=rh8m1zZ#H@AUDlGe4tMH z#r0#%lrfB%GKNTdB-_?(Ln#%k<`xkm%wIX5mlwVa+}|sIyjM>E%BDf^`K_jPJyqz^7(&=jnXGgRG~jly#vYINIp(pBp{Un@ zo59$A^~F~{4{m|t{Mqvy*l{2zTS00~Nw%b^1h|Zb*}^bcOdQQW%C78y_P&5na=e6l zckj_~tATTW-<%708AUiHj#$UXz|wA0^BAE}A8878GEf6+$fK{WbjpWtLi32{FyMDL zvd7uUIh4Z-ju*Nh&o|Phrm^|(=0K|*%7f>asjRlF;r@GIH)6IX;1N*~q)beq`f@cD z&Wdgm4i8hz&?iF!Efk#EcX8jxCV=GdWGgUPkJUeazCY{stRpp+i=#5cFR>6H(QCZxP%9K?cE3t0T#V#Z~RV_e2K#?Ks2NUsn|j+f|) z@0g8$wFVKB6l0W9HmGLxst9?OMCZ0o2rDR*|JHHhK^)cwTo51 zT*c+@yfY-tq_K3<(snPluLEA&zO<=nypjEXhOYQ)7q78qu3>Gc-l8ntbs+#ptm6;B zpY*K$UkY2IWO;r?{s+1N+`ol6CCG3+W4!`b-Hkfd%v}>m4%-g)I6G@|*R~77XF;n4 zXCiw?rhZoZ*?)BdfR^H}$!#hIp7a+)u;TTC@GD+%+pW1r>@VM6*0$NJu2*)m%$59q zRB{=@OmxccO?R3&{jbx+4~r-JciFy6AAV4Ovz{|Y&k&Oo!-j<$jO(G`7ENaDE#Lc1 z#}30%O8UU`MWSSD(CWGmN<omNfan?GE6K#nVqGZ}ncuV6Jdv^+gKknsr9pjKOY?`%9zM=@HVKYU>g7c N002ovPDHLkV1il$GLirQ delta 1256 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 literal 2785 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 YXR;y{u8a`{<0;@A+6MK?-0ki^0ji!RNdN!< diff --git a/resources/android/icon/drawable-xxxhdpi-icon.png b/resources/android/icon/drawable-xxxhdpi-icon.png index 53d8b39dbefde928dd4a789fc4ace2eb601a5bae..46f534be26c3bd2a10a8a72bb9a4563659ca49c4 100644 GIT binary patch literal 7425 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%0AS|sI(7J%A0k$N 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 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;