Progressive web app (#367)
* Basic PWA support * Theme color * Removed manifest reference added by angular cli * Added update notification * Basic install widget * Fixed annoying scrolling bug on mobile * Minor fixes * Minor tweaks * Better platform detection * iOS instructions * Success notification and viewport styling * Styling * Dark bg color * Another color change * Yet another color change * Small tweaks to update/install UX * Styling * Bugfix * Several Install widget fixes * Updated angular service-worker * Changed install widget * Caching strategies * Linting * Styling * PR comments
This commit is contained in:
parent
74d4907be2
commit
5cca259ae7
16 changed files with 391 additions and 39 deletions
10
angular.json
10
angular.json
|
|
@ -20,7 +20,8 @@
|
|||
"assets": [
|
||||
"src/assets",
|
||||
"src/pow.wasm",
|
||||
"src/404.html"
|
||||
"src/404.html",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.less"
|
||||
|
|
@ -71,7 +72,9 @@
|
|||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "ngsw-config.json"
|
||||
},
|
||||
"desktop": {
|
||||
"budgets": [
|
||||
|
|
@ -131,7 +134,8 @@
|
|||
],
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/pow.wasm"
|
||||
"src/pow.wasm",
|
||||
"src/manifest.webmanifest"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
|||
50
ngsw-config.json
Normal file
50
ngsw-config.json
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
|
||||
"index": "/index.html",
|
||||
"assetGroups": [
|
||||
{
|
||||
"name": "app",
|
||||
"installMode": "prefetch",
|
||||
"resources": {
|
||||
"files": [
|
||||
"/favicon.ico",
|
||||
"/index.html",
|
||||
"/manifest.webmanifest",
|
||||
"/*.css",
|
||||
"/*.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "assets",
|
||||
"installMode": "lazy",
|
||||
"updateMode": "prefetch",
|
||||
"resources": {
|
||||
"files": [
|
||||
"/assets/**",
|
||||
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"dataGroups": [
|
||||
{
|
||||
"name": "coingecko",
|
||||
"urls": ["https://api.coingecko.com/**"],
|
||||
"cacheConfig": {
|
||||
"maxSize": 3,
|
||||
"maxAge": "60d",
|
||||
"strategy": "freshness"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ninja",
|
||||
"urls": ["https://mynano.ninja/api/**"],
|
||||
"cacheConfig": {
|
||||
"maxSize": 50,
|
||||
"maxAge": "60d",
|
||||
"strategy": "freshness"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
64
package-lock.json
generated
64
package-lock.json
generated
|
|
@ -816,6 +816,14 @@
|
|||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@angular/service-worker": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-10.2.5.tgz",
|
||||
"integrity": "sha512-8gsI+sg84Oor83w2iyQ99Tmf3sEXXiLT9R/kJ0NuOzRkPW0GV//Hi0y1emTpnp+INzYlCgqRBdp45HlDnynYOA==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||
|
|
@ -2367,7 +2375,7 @@
|
|||
"@types/jasmine": {
|
||||
"version": "2.5.54",
|
||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.54.tgz",
|
||||
"integrity": "sha512-B9YofFbUljs19g5gBKUYeLIulsh31U5AK70F41BImQRHEZQGm4GcN922UvnYwkduMqbC/NH+9fruWa/zrqvHIg==",
|
||||
"integrity": "sha1-prXyrir7bgMHd06MfGCOA31JHGM=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jasminewd2": {
|
||||
|
|
@ -2415,7 +2423,7 @@
|
|||
"@types/qrcode": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-0.8.1.tgz",
|
||||
"integrity": "sha512-Z3Z5S+DS9vBGwH0wBN4544se8ogM5Y0+t7flmPxvhR4nj7zTvOy3PkNaCX5vwKXOgr6KeLfDEX5lW4fTZ+chIg==",
|
||||
"integrity": "sha1-V3bLXaKZz6fO4mEPWZl6ytkK040=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
|
|
@ -2933,7 +2941,7 @@
|
|||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
|
|
@ -3020,12 +3028,12 @@
|
|||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
"integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"integrity": "sha1-SzXClE8GKov82mZBB2A1D+nd/CE=",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
|
|
@ -3034,7 +3042,7 @@
|
|||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
|
|
@ -3188,7 +3196,7 @@
|
|||
"async-exit-hook": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
|
||||
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
|
||||
"integrity": "sha1-i9iwJLDsmxwBzMua+dspvXF9+vM=",
|
||||
"dev": true
|
||||
},
|
||||
"async-limiter": {
|
||||
|
|
@ -3463,7 +3471,7 @@
|
|||
"bignumber.js": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-5.0.0.tgz",
|
||||
"integrity": "sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg=="
|
||||
"integrity": "sha1-+85j8Jd2swAKgxhbrc3lJdrzSDM="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.1.0",
|
||||
|
|
@ -3754,7 +3762,7 @@
|
|||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -3908,7 +3916,7 @@
|
|||
"buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=",
|
||||
"requires": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
|
|
@ -3917,7 +3925,7 @@
|
|||
"buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
"integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA="
|
||||
},
|
||||
"buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
|
|
@ -4301,7 +4309,7 @@
|
|||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=",
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
|
|
@ -4774,7 +4782,7 @@
|
|||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
|
||||
"dev": true
|
||||
},
|
||||
"convert-source-map": {
|
||||
|
|
@ -5411,7 +5419,7 @@
|
|||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
"integrity": "sha1-xPp8lUBKF6nD6Mp+FTcxK3NjMKw="
|
||||
},
|
||||
"default-gateway": {
|
||||
"version": "4.2.0",
|
||||
|
|
@ -5627,7 +5635,7 @@
|
|||
"diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=",
|
||||
"dev": true
|
||||
},
|
||||
"diffie-hellman": {
|
||||
|
|
@ -7152,7 +7160,7 @@
|
|||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
"integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
|
|
@ -8107,7 +8115,7 @@
|
|||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
"integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.1.0",
|
||||
|
|
@ -9742,7 +9750,7 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -10474,7 +10482,7 @@
|
|||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
|
|
@ -12550,7 +12558,7 @@
|
|||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"integrity": "sha1-zZJL9SAKB1uDwYjNa54hG3/A0+0=",
|
||||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
|
|
@ -13017,7 +13025,7 @@
|
|||
"ripemd160": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||
"integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=",
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
|
|
@ -13100,7 +13108,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
"integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0="
|
||||
},
|
||||
"safe-regex": {
|
||||
"version": "1.1.0",
|
||||
|
|
@ -13114,7 +13122,7 @@
|
|||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
|
||||
},
|
||||
"sanitize-filename": {
|
||||
"version": "1.6.3",
|
||||
|
|
@ -13187,7 +13195,7 @@
|
|||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
"integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
|
|
@ -14319,7 +14327,7 @@
|
|||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
|
|
@ -15279,7 +15287,7 @@
|
|||
"uri-js": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||
"integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=",
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
|
|
@ -16439,7 +16447,7 @@
|
|||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||
"integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
|
|
@ -16458,7 +16466,7 @@
|
|||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"integrity": "sha1-rgdOa9wMFKQx6ATmJFScYzsABFc=",
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
"@angular/platform-browser": "^10.0.7",
|
||||
"@angular/platform-browser-dynamic": "^10.0.7",
|
||||
"@angular/router": "^10.0.7",
|
||||
"@angular/service-worker": "^10.2.5",
|
||||
"@ledgerhq/hw-transport": "^5.39.0",
|
||||
"@ledgerhq/hw-transport-node-ble": "^5.39.0",
|
||||
"@ledgerhq/hw-transport-node-hid": "^5.39.0",
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<app-install-widget></app-install-widget>
|
||||
|
||||
<div class="nav-search">
|
||||
<form class="uk-search uk-search-default uk-width-1-1">
|
||||
<a href="javascript:void(0)" (click)="performSearch()" class="uk-search-icon-flip" uk-search-icon></a>
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@ import {PriceService} from './services/price.service';
|
|||
import {NotificationService} from './services/notification.service';
|
||||
import {WorkPoolService} from './services/work-pool.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {SwUpdate} from '@angular/service-worker';
|
||||
import {RepresentativeService} from './services/representative.service';
|
||||
import {NodeService} from './services/node.service';
|
||||
import { DesktopService, LedgerService } from './services';
|
||||
import { environment } from 'environments/environment';
|
||||
import { DeeplinkService } from './services/deeplink.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
|
|
@ -29,6 +31,7 @@ export class AppComponent implements OnInit {
|
|||
public nodeService: NodeService,
|
||||
private representative: RepresentativeService,
|
||||
private router: Router,
|
||||
public updates: SwUpdate,
|
||||
private workPool: WorkPoolService,
|
||||
public price: PriceService,
|
||||
private desktop: DesktopService,
|
||||
|
|
@ -48,7 +51,6 @@ export class AppComponent implements OnInit {
|
|||
nanoPrice = this.price.price;
|
||||
fiatTimeout = 5 * 60 * 1000; // Update fiat prices every 5 minutes
|
||||
inactiveSeconds = 0;
|
||||
windowHeight = 1000;
|
||||
navExpanded = false;
|
||||
showAccountsDropdown = false;
|
||||
canToggleLightMode = true;
|
||||
|
|
@ -144,6 +146,21 @@ export class AppComponent implements OnInit {
|
|||
});
|
||||
this.desktop.send('deeplink-ready');
|
||||
|
||||
// Notify user if service worker update is available
|
||||
this.updates.available.subscribe((event) => {
|
||||
console.log(`SW update available. Current: ${event.current.hash}. New: ${event.available.hash}`);
|
||||
this.notifications.sendInfo(
|
||||
'An update was installed in the background and will be applied on next launch. <a href="#" (click)="applySwUpdate()">Apply immediately</a>',
|
||||
{ length: 10000 }
|
||||
);
|
||||
});
|
||||
|
||||
// Notify user after service worker was updated
|
||||
this.updates.activated.subscribe((event) => {
|
||||
console.log(`SW update successful. Current: ${event.current.hash}`);
|
||||
this.notifications.sendSuccess('Nault was updated successfully.');
|
||||
});
|
||||
|
||||
// Check how long the wallet has been inactive, and lock it if it's been too long
|
||||
setInterval(() => {
|
||||
this.inactiveSeconds += 1;
|
||||
|
|
@ -180,6 +197,10 @@ export class AppComponent implements OnInit {
|
|||
this.settings.setAppSetting('walletVersion', 2); // Update wallet version so we do not patch in the future.
|
||||
}
|
||||
|
||||
applySwUpdate() {
|
||||
this.updates.activateUpdate();
|
||||
}
|
||||
|
||||
toggleNav() {
|
||||
this.navExpanded = !this.navExpanded;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import { QrScanComponent } from './components/qr-scan/qr-scan.component';
|
|||
import {SignComponent} from './components/sign/sign.component';
|
||||
import {RemoteSigningComponent} from './components/remote-signing/remote-signing.component';
|
||||
import {RemoteSignService} from './services/remote-sign.service';
|
||||
import { InstallWidgetComponent } from './components/install-widget/install-widget.component';
|
||||
import { QrModalComponent } from './components/qr-modal/qr-modal.component';
|
||||
import { QrModalService } from './services/qr-modal.service';
|
||||
import { NgbModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
|
@ -64,6 +65,8 @@ import { ZXingScannerModule } from '@zxing/ngx-scanner';
|
|||
import { DeeplinkService, NinjaService } from './services';
|
||||
import { ConverterComponent } from './components/converter/converter.component';
|
||||
import { QrGeneratorComponent } from './components/qr-generator/qr-generator.component';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
import { MultisigComponent } from './components/multisig/multisig.component';
|
||||
import { KeygeneratorComponent } from './components/keygenerator/keygenerator.component';
|
||||
|
||||
|
|
@ -102,6 +105,7 @@ import { KeygeneratorComponent } from './components/keygenerator/keygenerator.co
|
|||
QrModalComponent,
|
||||
ConverterComponent,
|
||||
QrGeneratorComponent,
|
||||
InstallWidgetComponent,
|
||||
MultisigComponent,
|
||||
KeygeneratorComponent,
|
||||
],
|
||||
|
|
@ -115,6 +119,7 @@ import { KeygeneratorComponent } from './components/keygenerator/keygenerator.co
|
|||
ZXingScannerModule,
|
||||
NgbModule,
|
||||
PasswordStrengthMeterModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||
],
|
||||
providers: [
|
||||
UtilService,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
<div (click)="install()" [tabindex]="isIosInstallable() ? -1 : 0" class="nav-install-row half-muted" [class.interactable]="!isIosInstallable()" [class.large]="isIosInstallable()" [class.visible]="showInstallPromotion" [class.uk-hidden]="!isPromotable()">
|
||||
<span uk-icon="icon: download; ratio: 1.2" class="icon"></span>
|
||||
<div>
|
||||
<div class="label">Install Nault for {{ platform }}</div>
|
||||
<div class="description" *ngIf="isIosInstallable()">
|
||||
1. Tap the <span uk-icon="icon: push; ratio: 0.8"></span> Share button
|
||||
<span *ngIf="platform === 'iOS'">below</span>
|
||||
<span *ngIf="platform === 'iPadOS'">in the top right</span> <br/>
|
||||
2. Select <span uk-icon="icon: plus-circle; ratio: 0.8"></span> Add to home screen
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
.nav-install-row {
|
||||
width: 100%;
|
||||
height: 65px;
|
||||
border-bottom: 1px solid #16161b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
color: white;
|
||||
transition: all 1s ease-in-out;
|
||||
font-family: Montserrat, Arial, Helvetica, sans-serif;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
|
||||
&.half-muted {
|
||||
color: #b6bed5;
|
||||
|
||||
&.interactable:hover .label {
|
||||
border-color: #b6bed5;
|
||||
}
|
||||
}
|
||||
|
||||
&.interactable {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.large {
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.label {
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: border-color .1s ease-in-out;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #8f8fab;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&:not(.visible) {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InstallWidgetComponent } from './install-widget.component';
|
||||
|
||||
describe('InstallWidgetComponent', () => {
|
||||
let component: InstallWidgetComponent;
|
||||
let fixture: ComponentFixture<InstallWidgetComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ InstallWidgetComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InstallWidgetComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
105
src/app/components/install-widget/install-widget.component.ts
Normal file
105
src/app/components/install-widget/install-widget.component.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { NotificationService } from 'app/services/notification.service';
|
||||
|
||||
interface InstallEvent extends Event {
|
||||
userChoice: Promise<{ outcome: 'accepted' | 'dismissed', platform: string }>;
|
||||
prompt(): void;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-install-widget',
|
||||
templateUrl: './install-widget.component.html',
|
||||
styleUrls: ['./install-widget.component.less'],
|
||||
})
|
||||
export class InstallWidgetComponent implements OnInit {
|
||||
|
||||
installEvent: InstallEvent;
|
||||
showInstallPromotion = false;
|
||||
platform = this.getPlatform();
|
||||
promotablePlatforms = ['Android', 'iOS', 'iPadOS', 'Chrome OS'];
|
||||
|
||||
constructor(
|
||||
private notifications: NotificationService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.isPromotable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show clickable installation banner (Chrome / Edge only)
|
||||
window.addEventListener('beforeinstallprompt', (event: InstallEvent) => {
|
||||
// Prevent the mini-infobar from appearing on mobile
|
||||
event.preventDefault();
|
||||
|
||||
this.installEvent = event;
|
||||
this.showInstallPromotion = true;
|
||||
});
|
||||
|
||||
// Fallback for iOS family
|
||||
if (this.isIosInstallable()) {
|
||||
setTimeout(() => {
|
||||
this.showInstallPromotion = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
install() {
|
||||
if (!this.installEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.installEvent.prompt();
|
||||
this.installEvent.userChoice.then((result) => {
|
||||
if (result.outcome === 'accepted') {
|
||||
this.notifications.sendSuccess('Nault was successfully installed to the device.');
|
||||
this.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.showInstallPromotion = false;
|
||||
}
|
||||
|
||||
getPlatform() {
|
||||
const platform = window.navigator.platform;
|
||||
const userAgent = window.navigator.userAgent;
|
||||
|
||||
if (platform.includes('Win')) {
|
||||
return 'Windows';
|
||||
} else if (platform.includes('Mac')) {
|
||||
return 'Mac';
|
||||
} else if (userAgent.includes('Android')) {
|
||||
return 'Android';
|
||||
} else if (userAgent.includes('CrOS')) {
|
||||
return 'Chrome OS';
|
||||
} else if (platform.includes('Linux')) {
|
||||
return 'Linux';
|
||||
} else if (platform.includes('iPhone')) {
|
||||
return 'iOS';
|
||||
} else if (platform.includes('iPad')) {
|
||||
return 'iPadOS';
|
||||
}
|
||||
}
|
||||
|
||||
isIosInstallable() {
|
||||
if (!this.isPromotable() || this.isInstalled()) {
|
||||
return false;
|
||||
}
|
||||
return this.platform === 'iOS' || this.platform === 'iPadOS' && 'standalone' in window.navigator;
|
||||
}
|
||||
|
||||
isPromotable() {
|
||||
if (this.isInstalled()) {
|
||||
return false;
|
||||
}
|
||||
return this.promotablePlatforms.includes(this.platform);
|
||||
}
|
||||
|
||||
isInstalled() {
|
||||
return window.matchMedia('(display-mode: standalone)').matches // Chrome & Edge
|
||||
|| (window.navigator as any).standalone === true // Safari
|
||||
|| window.navigator.userAgent.includes('Electron'); // Electron
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,8 @@
|
|||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
"theme_color": "#4a90e2",
|
||||
"background_color": "#1f1f24",
|
||||
"display": "standalone",
|
||||
"start_url": "/"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
<meta name="application-name" content="Nault">
|
||||
<meta name="msapplication-TileColor" content="#4a90e2">
|
||||
<meta name="msapplication-config" content="assets/favicon/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<meta name="theme-color" content="#4a90e2">
|
||||
</head>
|
||||
<body class="body" style="margin: 0;">
|
||||
<app-root>
|
||||
|
|
@ -48,7 +48,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</app-root>
|
||||
<script type="text/javascript" src="assets/lib/pow/startThreads.js" async></script>
|
||||
<script type="text/javascript" src="assets/lib/pow/nano-webgl-pow.js" async></script>
|
||||
<script type="text/javascript" src="assets/lib/pow/startThreads.js" async=""></script>
|
||||
<script type="text/javascript" src="assets/lib/pow/nano-webgl-pow.js" async=""></script>
|
||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: @nav-background-color;
|
||||
color: #868AB0;
|
||||
padding-left: 0;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ h1, h2, h3, h4, h5, .uk-text-lead, .uk-button, .uk-alert, .uk-description-list d
|
|||
border-radius: @global-border-radius-medium;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
@media (max-width: 939px) {
|
||||
.nlt-button-group button {
|
||||
margin-bottom: @nlt-intro-margin / 2 !important;
|
||||
|
|
@ -195,12 +199,14 @@ input[type=number] {
|
|||
}
|
||||
|
||||
.app-grid {
|
||||
// position: relative;
|
||||
margin-top: 0;
|
||||
overflow: hidden;
|
||||
margin-left: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
.content-container {
|
||||
background: #FFF;
|
||||
padding: 15px 20px;
|
||||
|
|
@ -217,7 +223,7 @@ input[type=number] {
|
|||
margin-top: @mobile-top-bar-height;
|
||||
height: calc(100vh - @mobile-top-bar-height);
|
||||
padding: 15px 15px;
|
||||
transition: transform 0.5s ease;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&.nav-expanded {
|
||||
transform: translateX(360px);
|
||||
|
|
|
|||
59
src/manifest.webmanifest
Normal file
59
src/manifest.webmanifest
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"name": "nault",
|
||||
"short_name": "nault",
|
||||
"theme_color": "#1976d2",
|
||||
"background_color": "#1f1f24",
|
||||
"display": "standalone",
|
||||
"scope": "./",
|
||||
"start_url": "./",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue