diff --git a/Procfile b/Procfile new file mode 100644 index 00000000000..c508e9c5b2f --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node heroku.js \ No newline at end of file diff --git a/app-template/bitpay/img/192x192.png b/app-template/bitpay/img/192x192.png new file mode 100644 index 00000000000..140ff44475f Binary files /dev/null and b/app-template/bitpay/img/192x192.png differ diff --git a/app-template/copay/img/192x192.png b/app-template/copay/img/192x192.png new file mode 100644 index 00000000000..7b2bc036ff9 Binary files /dev/null and b/app-template/copay/img/192x192.png differ diff --git a/app-template/index-template.html b/app-template/index-template.html index 72735d05b14..a72e7d7f381 100644 --- a/app-template/index-template.html +++ b/app-template/index-template.html @@ -11,10 +11,17 @@ - - + + + + + @@ -28,6 +35,8 @@ + + @@ -40,7 +49,7 @@ - + \ No newline at end of file diff --git a/app-template/manifest.ionic-template.json b/app-template/manifest.ionic-template.json index 2fdc379b07b..24ade88d50d 100644 --- a/app-template/manifest.ionic-template.json +++ b/app-template/manifest.ionic-template.json @@ -8,8 +8,18 @@ "src": "assets/img/app/logo.png", "sizes": "150x65", "type": "image/png" + }, + { + "src": "assets/img/app/192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "assets/img/app/logo.png", + "sizes": "512x512", + "type": "image/png" } ], - "background_color": "*THEMECOLOR*", - "theme_color": "*THEMECOLOR*" -} + "background_color": "#ffffff", + "theme_color": "#ffffff" +} \ No newline at end of file diff --git a/heroku.js b/heroku.js new file mode 100644 index 00000000000..db774dabd9c --- /dev/null +++ b/heroku.js @@ -0,0 +1,34 @@ +var express = require('express'); +var app = express(); +var path = require('path'); +var bodyParser = require('body-parser'); +var cors = require('cors'); + +app.get('*', (req, res, next) => { + if ( + req.headers['x-forwarded-proto'] !== 'https' && + process.env.NODE_ENV === 'production' + ) + res.redirect('https://' + req.headers.host + req.url); + else next(); /* Continue to other routes if we're not redirecting */ +}); + +app.use(bodyParser.urlencoded({ extended: 'true' })); +app.use(bodyParser.json()); +app.use(cors()); + +app.use(function(req, res, next) { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'DELETE, PUT'); + res.header( + 'Access-Control-Allow-Headers', + 'Origin, X-Requested-With, Content-Type, Accept' + ); + next(); +}); + +app.use(express.static(path.join(__dirname, 'www'))); +app.set('port', process.env.PORT || 5000); +app.listen(app.get('port'), function() { + console.log('Express server listening on port ' + app.get('port')); +}); diff --git a/ngsw-worker.js b/ngsw-worker.js new file mode 100644 index 00000000000..599f73fda7d --- /dev/null +++ b/ngsw-worker.js @@ -0,0 +1,18 @@ +// This is the "Offline copy of assets" service worker + +const CACHE = "pwabuilder-offline"; + +importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js'); + +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "SKIP_WAITING") { + self.skipWaiting(); + } +}); + +workbox.routing.registerRoute( + new RegExp('/*'), + new workbox.strategies.StaleWhileRevalidate({ + cacheName: CACHE + }) +); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a06f578ce7e..40868ee97a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1983,6 +1983,14 @@ "tslib": "^1.7.1" } }, + "@angular/service-worker": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-5.2.10.tgz", + "integrity": "sha512-VGfX+S7ewQBYRDB3BB68p+WbAcFmvwzU3PesSqi/N7qSsh2pnxxFne71UoNS5YlJo0Qt2YKIC2bslDBQk7Y2rg==", + "requires": { + "tslib": "^1.7.1" + } + }, "@angular/tsc-wrapped": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-4.4.6.tgz", @@ -5735,6 +5743,14 @@ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", "dev": true }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -15083,6 +15099,18 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", diff --git a/package.json b/package.json index b54637e8d81..64de5944844 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,10 @@ "multisignature", "bitcore" ], + "engines": { + "node": "10.9.0", + "npm": "6.4.1" + }, "main": "electron/main.js", "title": "Copay", "user-agent": "%name %ver (%osinfo)", @@ -32,6 +36,8 @@ }, "scripts": { "postinstall": "npm run env:dev && npm run prompt", + "heroku-postbuild": "npm run apply:bitpay && npm run build:desktop && npm run ngsw-apply", + "ngsw-apply": "cp ngsw-worker.js www/", "electron:patch": "(! grep -q 'com.apple.security.app-sandbox' ./node_modules/app-builder-lib/templates/entitlements.mac.plist && sed -i -e \"// a\\ \ncom.apple.security.app-sandbox\" ./node_modules/app-builder-lib/templates/entitlements.mac.plist) | echo 'electron patched.'", "prompt": "\n\n\n\n\n\n#\n# Choose a distribution with:\n#\n# $ npm run apply:copay\n# or\n# $ npm run apply:bitpay\n#\n", "start": "npm run ionic:serve", @@ -101,6 +107,7 @@ "@angular/http": "5.2.10", "@angular/platform-browser": "5.2.10", "@angular/platform-browser-dynamic": "5.2.10", + "@angular/service-worker": "5.2.10", "@angular/tsc-wrapped": "4.4.6", "@ionic-native/android-fingerprint-auth": "4.14.0", "@ionic-native/clipboard": "4.14.0", @@ -272,4 +279,4 @@ "prettier": { "singleQuote": true } -} +} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2bd8d931f40..c0eb1438a1f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,6 @@ import { HttpClientModule } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ServiceWorkerModule } from '@angular/service-worker'; import { IonicImageLoader } from 'ionic-image-loader'; import { MarkdownModule } from 'ngx-markdown'; import { NgxTextOverflowClampModule } from 'ngx-text-overflow-clamp'; @@ -135,7 +136,11 @@ export class MyMissingTranslationHandler implements MissingTranslationHandler { useClass: LanguageLoader } }), - ZXingScannerModule.forRoot() + ZXingScannerModule.forRoot(), + /* Enable Service Workers for Production */ + ServiceWorkerModule.register('./ngsw-worker.js', { + enabled: true + }) ], bootstrap: [IonicApp], entryComponents: [CopayApp, ...PAGES, ...COMPONENTS],