Merge branch 'wallet/sprint/18' into wallet/task/399

This commit is contained in:
Jean-Baptiste Dominguez 2018-07-03 12:17:56 +09:00 committed by GitHub
commit 1d9efcca06
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 808 additions and 157 deletions

View file

@ -8,6 +8,21 @@ module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
exec: {
get_nwjs_for_pkg: {
command: 'if [ ! -d ./cache/0.19.5-pkg/osx64/nwjs.app ]; then cd ./cache; curl https://dl.nwjs.io/v0.19.5-mas-beta/nwjs-mas-v0.19.5-osx-x64.zip --output nwjs.zip; unzip nwjs.zip; mkdir -p ./0.19.5-pkg/osx64; cp -R ./nwjs-mas-v0.19.5-osx-x64/nwjs.app ./0.19.5-pkg/osx64/; fi'
},
create_others_dist: {
command: 'sh webkitbuilds/create-others-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
create_dmg_dist: {
command: 'sh webkitbuilds/create-dmg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
create_pkg_dist: {
command: 'sh webkitbuilds/create-pkg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"'
},
sign_desktop_dist: {
command: 'sh webkitbuilds/sign-desktop-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>"'
},
appConfig: {
command: 'node ./util/buildAppConfig.js'
},
@ -20,9 +35,6 @@ module.exports = function(grunt) {
cordovaclean: {
command: 'make -C cordova clean'
},
macos: {
command: 'sh webkitbuilds/build-macos.sh sign'
},
coveralls: {
command: 'cat coverage/report-lcov/lcov.info |./node_modules/coveralls/bin/coveralls.js'
},
@ -61,7 +73,7 @@ module.exports = function(grunt) {
stdin: true,
},
desktopsign: {
cmd: 'gpg -u E0AE67E7 --output webkitbuilds/<%= pkg.title %>-linux.zip.sig --detach-sig webkitbuilds/<%= pkg.title %>-linux.zip ; gpg -u E0AE67E7 --output webkitbuilds/<%= pkg.title %>.exe.sig --detach-sig webkitbuilds/<%= pkg.title %>.exe'
cmd: 'gpg -u E0AE67E7 --output webkitbuilds/others/<%= pkg.title %>-linux.zip.sig --detach-sig webkitbuilds/others/<%= pkg.title %>-linux.zip ; gpg -u E0AE67E7 --output webkitbuilds/others/<%= pkg.title %>.exe.sig --detach-sig webkitbuilds/others/<%= pkg.title %>.exe'
},
desktopverify: {
cmd: 'gpg --verify webkitbuilds/<%= pkg.title %>-linux.zip.sig webkitbuilds/<%= pkg.title %>-linux.zip; gpg --verify webkitbuilds/<%= pkg.title %>.exe.sig webkitbuilds/<%= pkg.title %>.exe'
@ -124,6 +136,7 @@ module.exports = function(grunt) {
},
angular: {
src: [
'src/shim/shim.js',
'bower_components/qrcode-generator/js/qrcode.js',
'bower_components/qrcode-generator/js/qrcode_UTF8.js',
'bower_components/moment/min/moment-with-locales.js',
@ -152,6 +165,7 @@ module.exports = function(grunt) {
src: [
'src/js/app.js',
'src/js/routes.js',
'src/js/decorators/*.js',
'src/js/directives/*.js',
'!src/js/directives/*.spec.js',
@ -233,38 +247,78 @@ module.exports = function(grunt) {
expand: true,
cwd: 'webkitbuilds/',
src: ['.desktop', '../www/img/app/favicon.ico', '../resources/<%= pkg.name %>/linux/512x512.png'],
dest: 'webkitbuilds/<%= pkg.title %>/linux64/',
dest: 'webkitbuilds/others/<%= pkg.title %>/linux64/',
flatten: true,
filter: 'isFile'
}],
}
},
nwjs: {
options: {
appName: '<%= pkg.title %>',
platforms: ['win64', 'osx64', 'linux64'],
buildDir: './webkitbuilds',
version: '0.19.5',
macIcns: './resources/<%= pkg.name %>/mac/app.icns',
exeIco: './www/img/app/logo.ico',
macPlist: {
'CFBundleURLTypes': [
{
'CFBundleURLName': 'URI Handler',
'CFBundleURLSchemes': ['bitcoin', '<%= pkg.name %>']
}
]
}
others: {
options: {
appName: '<%= pkg.nameCaseNoSpace %>',
platforms: ['win64', 'linux64'],
buildDir: './webkitbuilds/others',
version: '0.19.5',
exeIco: './www/img/app/logo.ico'
},
src: ['./package.json', './www/**/*']
},
dmg: {
options: {
appName: '<%= pkg.nameCaseNoSpace %>',
platforms: ['osx64'],
buildDir: './webkitbuilds/dmg',
version: '0.19.5',
macIcns: './resources/<%= pkg.name %>/mac/app.icns',
exeIco: './www/img/app/logo.ico',
macPlist: {
'CFBundleDisplayName': '<%= pkg.title %>',
'CFBundleShortVersionString': '<%= pkg.version %>',
'CFBundleVersion': '<%= pkg.androidVersion %>',
'LSApplicationCategoryType': 'public.app-category.finance',
'CFBundleURLTypes': [
{
'CFBundleURLName': 'URI Handler',
'CFBundleURLSchemes': ['bitcoin', '<%= pkg.name %>']
}
]
}
},
src: ['./package.json', './www/**/*']
},
pkg: {
options: {
appName: '<%= pkg.nameCaseNoSpace %>',
platforms: ['osx64'],
buildDir: './webkitbuilds/pkg',
version: '0.19.5',
macIcns: './resources/<%= pkg.name %>/mac/pkg/app.icns',
exeIco: './www/img/app/logo.ico',
macPlist: {
'CFBundleIdentifier': 'com.bitcoin.mwallet.mac',
'CFBundleDisplayName': '<%= pkg.title %>',
'CFBundleShortVersionString': '<%= pkg.version %>',
'CFBundleVersion': '<%= pkg.androidVersion %>',
'LSApplicationCategoryType': 'public.app-category.finance',
'CFBundleURLTypes': [
{
'CFBundleURLName': 'URI Handler',
'CFBundleURLSchemes': ['bitcoin', '<%= pkg.name %>']
}
]
}
},
src: ['./package.json', './www/**/*']
},
src: ['./package.json', './www/**/*']
},
compress: {
linux: {
options: {
archive: './webkitbuilds/<%= pkg.title %>-linux.zip'
archive: './webkitbuilds/others/<%= pkg.title %>-linux.zip'
},
expand: true,
cwd: './webkitbuilds/<%= pkg.title %>/linux64/',
cwd: './webkitbuilds/others/<%= pkg.title %>/linux64/',
src: ['**/*'],
dest: '<%= pkg.title %>-linux/'
}
@ -283,9 +337,6 @@ module.exports = function(grunt) {
grunt.registerTask('default', ['nggettext_compile', 'exec:appConfig', 'exec:externalServices', 'browserify', 'sass', 'concat', 'copy:ionic_fonts', 'copy:ionic_js']);
grunt.registerTask('prod', ['default', 'uglify']);
grunt.registerTask('translate', ['nggettext_extract']);
grunt.registerTask('desktop', ['prod', 'nwjs', 'copy:linux', 'compress:linux']);
grunt.registerTask('osx', ['prod', 'nwjs', 'exec:macos', 'exec:osxsign']);
grunt.registerTask('osx-debug', ['default', 'nwjs']);
grunt.registerTask('chrome', ['default','exec:chrome']);
grunt.registerTask('wp', ['prod', 'exec:wp']);
grunt.registerTask('wp-copy', ['default', 'exec:wpcopy']);
@ -299,4 +350,21 @@ module.exports = function(grunt) {
grunt.registerTask('android-release', ['prod', 'exec:android', 'exec:androidsign']);
grunt.registerTask('desktopsign', ['exec:desktopsign', 'exec:desktopverify']);
// Build desktop
grunt.registerTask('desktop-build', ['desktop-others', 'desktop-osx-dmg', 'desktop-osx-pkg']);
// Build desktop win64 & linux64
grunt.registerTask('desktop-others', ['prod', 'nwjs:others', 'copy:linux', 'exec:create_others_dist']);
// Build desktop osx pkg
grunt.registerTask('desktop-osx-pkg', ['prod', 'exec:get_nwjs_for_pkg', 'nwjs:pkg', 'exec:create_pkg_dist']);
// Build desktop osx dmg
grunt.registerTask('desktop-osx-dmg', ['prod', 'nwjs:dmg', 'exec:create_dmg_dist']);
// Sign desktop
grunt.registerTask('desktop-sign', ['exec:sign_desktop_dist']);
// Release desktop
grunt.registerTask('desktop-release', ['desktop-build', 'desktop-sign']);
};

View file

@ -11,7 +11,10 @@ var templates = {
'ionic.config.json': '/',
'.desktop': 'webkitbuilds/',
'setup-win.iss': 'webkitbuilds/',
'build-macos.sh': 'webkitbuilds/',
'create-dmg-dist.sh': 'webkitbuilds/',
'create-others-dist.sh': 'webkitbuilds/',
'create-pkg-dist.sh': 'webkitbuilds/',
'sign-desktop-dist.sh': 'webkitbuilds/',
'manifest.json': 'chrome-app/',
// 'bower.json': '/',
};

View file

@ -2,7 +2,7 @@
"packageName": "bitcoin.com",
"packageDescription": "Bitcoin.com Wallet",
"packageNameId": "com.bitcoin.mwallet",
"userVisibleName": "Bitcoin.com",
"userVisibleName": "Bitcoin.com Wallet",
"purposeLine": "Bitcoin.com Wallet",
"bundleName": "bitcoincom",
"appUri": "bitcoincom",
@ -18,7 +18,7 @@
"appDescription": "Bitcoin.com Wallet",
"winAppName": "BitcoinWallet",
"WindowsStoreIdentityName": "18C7659D.Bitcoin.com-SecureBitcoinWallet",
"WindowsStoreDisplayName": "Bitcoin.com - Secure Bitcoin Wallet",
"WindowsStoreDisplayName": "Bitcoin.com Wallet",
"wpPublisherId": "{31cdd08b-457c-413d-b440-f6665eec847d}",
"wpProductId": "{5381aa50-9069-11e4-84cc-293caf9cbdc8}",
"windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c",

View file

@ -75,8 +75,6 @@
<plugin name="cordova-plugin-media" spec="~5.0.2">
<variable name="KEEP_AVAUDIOSESSION_ALWAYS_ACTIVE" value="NO" />
</plugin>
<!-- Changes in error descriptions may break the use of cordova-plugin-secure-storage -->
<plugin name="cordova-plugin-secure-storage" spec="2.6.8" />
<!-- Supported Platforms -->
<engine name="ios" spec="~4.5.3" />
<engine name="android" spec="~6.3.0" />

View file

@ -1,11 +1,5 @@
#!/bin/bash
SHOULD_SIGN=$1
if [ "$SHOULD_SIGN" ]
then
echo "Will sign the APP"
fi
# by Andy Maloney
# http://asmaloney.com/2013/07/howto/packaging-a-mac-os-x-application-using-a-dmg/
@ -16,21 +10,25 @@ if [ -d "$dir" ]; then
fi
# set up your app name, architecture, and background image file name
APP_NAME="*USERVISIBLENAME*"
APP_PACKAGE=$1
APP_VERSION=$2
APP_NAME=$3
APP_FULLNAME=$4
rm dmg-background.tiff
ln -s ../resources/*PACKAGENAME*/mac/dmg-background.tiff dmg-background.tiff
ln -s ../resources/bitcoin.com/mac/dmg-background.tiff dmg-background.tiff
rm volume-icon.icns
ln -s ../resources/*PACKAGENAME*/mac/volume-icon.icns volume-icon.icns
ln -s ../resources/bitcoin.com/mac/volume-icon.icns volume-icon.icns
DMG_VOLUME_ICON="volume-icon.icns"
DMG_BACKGROUND_IMG="dmg-background.tiff"
PATH_NAME="${APP_NAME}/osx64/"
PATH_NAME="dmg/${APP_NAME}/osx64/"
# you should not need to change these
APP_EXE="${PATH_NAME}${APP_NAME}.app/Contents/MacOS/nwjs"
VOL_NAME="${APP_NAME}"
DMG_TMP="${VOL_NAME}-temp.dmg"
DMG_FINAL="${VOL_NAME}.dmg"
DMG_TMP="dmg/${VOL_NAME}-temp.dmg"
DMG_FINAL="dmg/${VOL_NAME}.dmg"
STAGING_DIR="tmp"
# Check the background image DPI and convert it if it isn't 72x72
@ -66,25 +64,6 @@ SIZE=250
if [ $? -ne 0 ]; then
echo "Error: Cannot compute size of staging dir"
exit
fi
# Sign Code (MATIAS)
if [ $SHOULD_SIGN ]
then
echo "Signing ${APP_NAME} DMG"
export IDENTITY="3rd Party Mac Developer Application: BitPay, Inc. (884JRH5R93)"
# not need for 'out of app store' distribution (?)
# export PARENT_PLIST=parent.plist
# export CHILD_PLIST=child.plist
export APP_PATH=${STAGING_DIR}/${APP_NAME}.app
codesign --deep -s "${IDENTITY}" $APP_PATH"/Contents/Versions/52.0.2743.82/nwjs Helper.app" && echo "Sign 1"
codesign --deep -s "${IDENTITY}" $APP_PATH"/Contents/Versions/52.0.2743.82/nwjs Framework.framework/Resources/app_mode_loader.app" && echo "Sign 2"
codesign --deep -s "${IDENTITY}" $APP_PATH && echo "Sign 3"
echo "Signing Done"
fi
# create the temp DMG file
@ -175,6 +154,14 @@ hdiutil detach "${DEVICE}"
echo "Creating compressed image"
hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}"
export DIST_PATH="dist"
if [ ! -d $DIST_PATH ]; then
mkdir $DIST_PATH
fi
cp -vR "${DMG_FINAL}" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg"
# clean up
rm -rf "${DMG_TMP}"
rm -rf "${STAGING_DIR}"

View file

@ -0,0 +1,54 @@
#!/bin/bash
# make sure we are in the correct dir when we double-click a .command file
dir=${0%/*}
if [ -d "$dir" ]; then
cd "$dir"
fi
# set up your app name, architecture, and background image file name
APP_PACKAGE=$1
APP_VERSION=$2
APP_NAME=$3
APP_FULLNAME=$4
export APP_LINUX_PATH="others/${APP_NAME}/linux64"
export APP_WIN_PATH="others/${APP_NAME}/win64"
export DIST_PATH="dist"
if [ ! -d $DIST_PATH ]; then
mkdir $DIST_PATH
fi
##
# LINUX
echo "Building Linux..."
# Building package
cp -R $APP_LINUX_PATH bitcoin-com-wallet
tar -cvzf "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz" bitcoin-com-wallet
# Clean
rm -R bitcoin-com-wallet
echo "Linux Done."
##
# WINDOWS
echo "Building Windows..."
# Building package
cp -R $APP_WIN_PATH bitcoin-com-wallet
zip -r "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip" bitcoin-com-wallet
# Clean
rm -R bitcoin-com-wallet
echo "Windows Done."
echo "Done."
exit

View file

@ -0,0 +1,45 @@
#!/bin/bash
# make sure we are in the correct dir when we double-click a .command file
dir=${0%/*}
if [ -d "$dir" ]; then
cd "$dir"
fi
# set up your app name, architecture, and background image file name
APP_PACKAGE=$1
APP_VERSION=$2
APP_NAME=$3
APP_FULLNAME=$4
rm entitlements-child.plist
ln -s ../resources/bitcoin.com/mac/pkg/entitlements-child.plist entitlements-child.plist
rm entitlements-parent.plist
ln -s ../resources/bitcoin.com/mac/pkg/entitlements-parent.plist entitlements-parent.plist
rm build.cfg
ln -s ../resources/bitcoin.com/mac/pkg/build.cfg build.cfg
rm build_mas.py
ln -s ../resources/bitcoin.com/mac/pkg/build_mas.py build_mas.py
echo "Signing ${APP_NAME}"
export APP_PATH="pkg/${APP_NAME}/osx64/${APP_NAME}"
export TMP_PATH="tmp"
export DIST_PATH="dist"
rm -rf $TMP_PATH
mkdir $TMP_PATH
if [ ! -d $DIST_PATH ]; then
mkdir $DIST_PATH
fi
python build_mas.py -C build.cfg -O "${TMP_PATH}/${APP_NAME}.app" -I "${APP_PATH}.app" -P "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg"
echo "Signing Done"
echo "Done."
exit

View file

@ -3,6 +3,7 @@
"description": "*DESCRIPTION*",
"author": "BitPay",
"version": "*VERSION*",
"androidVersion": "*ANDROIDVERSION*",
"fullVersion": "*FULLVERSION*",
"keywords": [
"bitcoin",
@ -14,8 +15,9 @@
],
"main": "www/index.html",
"title": "*USERVISIBLENAME*",
"nameCaseNoSpace": "*NAMECASENOSPACE*",
"window": {
"title": "*USERVISIBLENAME* - *PURPOSELINE*",
"title": "*USERVISIBLENAME*",
"icon": "www/img/app/icon.png",
"toolbar": false,
"show": true,
@ -69,6 +71,8 @@
"grunt-angular-gettext": "^2.2.3",
"grunt-browserify": "^5.0.0",
"grunt-cli": "^1.2.0",
"grunt-curl": "^2.4.1",
"grunt-zip": "^0.17.1",
"grunt-contrib-compress": "^1.3.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-copy": "^1.0.0",
@ -99,15 +103,19 @@
"build:ios-release": "cordova prepare ios && cordova build ios --release",
"build:android-release": "cordova prepare android && cordova build android --release",
"build:windows-release": "cordova prepare windows && cordova build windows --release --arch=\"ARM\"",
"build:desktop": "grunt desktop",
"build:osx": "grunt osx",
"build:desktop-release": "grunt desktop-release",
"build:desktop": "grunt desktop-build",
"build:osx-pkg": "grunt desktop-osx-pkg",
"build:osx-dmg": "grunt desktop-osx-dmg",
"build:others": "grunt desktop-others",
"sign:desktop": "grunt desktop-sign",
"open:ios": "open platforms/ios/*.xcodeproj",
"open:android": "open -a open -a /Applications/Android\\ Studio.app platforms/android",
"final:www": "npm run build:www-release",
"final:ios": "npm run final:www && npm run build:ios-release && npm run open:ios",
"final:android": "npm run final:www && npm run build:android-release && npm run sign:android && npm run run:android-release",
"final:windows": "npm run final:www && npm run build:windows-release",
"final:desktop": "npm run build:desktop && npm run build:osx",
"final:desktop": "npm run final:www && npm run build:desktop-release",
"run:android": "cordova run android --device",
"run:android-release": "cordova run android --device --release",
"log:android": "adb logcat | grep chromium",

View file

@ -0,0 +1,40 @@
#!/bin/bash
# make sure we are in the correct dir when we double-click a .command file
dir=${0%/*}
if [ -d "$dir" ]; then
cd "$dir"
fi
APP_PACKAGE=$1
APP_VERSION=$2
export DIST_PATH="dist"
##
# INIT GPG (YOU NEED THE PRIVATE KEY CONNECTED TO YOUR DESKTOP)
# gpg --card-edit
##
# LINUX
# Sig tar.gz
gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz"
gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz"
##
# WINDOWS
# Sig zip
gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip"
gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip"
##
# OSX
# Sig dmg
gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg"
gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg"
# Sig pkg
gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg"
gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg"

Binary file not shown.

View file

@ -0,0 +1,32 @@
[Sign]
## [REQUIRED] Your Application Certificate Identity
ApplicationIdentity = 3rd Party Mac Developer Application: Saint Bitts LLC (299HJ3G3BP)
## [OPTIONAL] Parent entitlements file
ParentEntitlements = entitlements-parent.plist
## [OPTIONAL] Child entitlements file
ChildEntitlements = entitlements-child.plist
## [OPTIONAL] Sandbox. Default: Yes
Sandbox = Yes
[Package]
## [REQUIRED for --pkg] Your Installer Certificate Identity
InstallerIdentity = 3rd Party Mac Developer Installer: Saint Bitts LLC (299HJ3G3BP)
## [OPTIONAL for --pkg] Installation path
InstallPath = /Applications
[Info.plist]
## [OPTIONAL] Your app bundle identifier
CFBundleIdentifier = com.bitcoin.mwallet.mac
## [REQUIRED] Team ID obtained from Apple Developer -> Membership -> Team ID
NWTeamID = 299HJ3G3BP
## Properties of Info.plist will be overwritten in this section.
[Resources]
## [OPTIONAL] Your custom icon file
Icon = ../resources/bitcoin.com/mac/pkg/app.icns
## [OPTIONAL] Locales
## If Locales is not set, all current locales are preserved.
## If comma separated locale list (e.g. en,fr,zh_CN) is given, you should have
## additional [Locale locale_name] section for each locale containing localized strings.
## Locales not in the list will be removed.
Locales = en

View file

@ -0,0 +1,253 @@
#!/usr/bin/env python
import argparse
import ConfigParser
import shutil
import os
import fnmatch
import plistlib
import tempfile
from datetime import datetime
import sys
import io
bundleid = None
verbose = False
def info(msg):
global verbose
if verbose:
print '[INFO] %s' % msg
def error(msg):
print '[ERROR] %s' % msg
print '\nFailed.'
sys.exit(1)
def system(cmd):
info(cmd)
os.system(cmd)
def check_options(config, section, required_options, msg):
missed_options = []
for option in required_options:
if not config.has_option(section, option):
missed_options.append(option)
if len(missed_options) != 0:
error(msg % (section, ', '.join(missed_options)))
def glob(pathname, pattern, returnOnFound=False):
matches = []
for root, dirnames, filenames in os.walk(pathname):
for dirname in fnmatch.filter(dirnames, pattern):
if returnOnFound:
return os.path.join(root, dirname)
matches.append(os.path.join(root, dirname))
for filename in fnmatch.filter(filenames, pattern):
if returnOnFound:
return os.path.join(root, filename)
matches.append(os.path.join(root, filename))
return matches
def get_bundle_id(args):
global bundleid
if bundleid is None:
plist = plistlib.readPlist(os.path.join(args.output, 'Contents/Info.plist'))
bundleid = plist['CFBundleIdentifier']
return bundleid
def get_from_info_plist(args, key, default=None):
plist = plistlib.readPlist(os.path.join(args.output, 'Contents/Info.plist'))
if key in plist:
return plist[key]
else:
return default
def patch_info_plist_file(file, replaces):
plist = plistlib.readPlist(file)
for (key, val) in replaces:
plist[key] = val
plistlib.writePlist(plist, file)
def generate_infoplist_strings_file(file, items):
with io.open(file, 'w', encoding='utf-16') as fd:
for item in items:
fd.write(unicode('%s = "%s";\n' % item, 'utf-8'))
def read_config(args):
print '\nParsing config file %s' % args.config_file
if not os.path.isfile(args.config_file):
error('%s does not exist' % args.config_file)
config = ConfigParser.SafeConfigParser()
config.optionxform = str # set to str to prevent transforming into lower cases
config.read(args.config_file)
check_options(config, 'Sign', ['ApplicationIdentity'], 'Missed options in [%s]: %s')
if args.pkg:
check_options(config, 'Package', ['InstallerIdentity'], 'Missed options for --pkg in [%s]: %s')
return config
def copy_to_output(args):
print '\nCopying %s to %s' % (args.input, args.output)
shutil.rmtree(args.output, ignore_errors=True)
shutil.copytree(args.input, args.output, symlinks=True) # symblic links are required
def patch_info_plist(config, args):
print '\nPatching Info.plist files'
replaces = []
for (key, val) in config.items('Info.plist'):
replaces.append((key, val))
file = os.path.join(args.output, 'Contents/Info.plist')
info(file)
patch_info_plist_file(file, replaces)
info_plist_files = glob(os.path.join(args.output, 'Contents/Versions'), 'Info.plist')
for file in info_plist_files:
if 'nwjs Framework' in file:
tmp_replaces = [('CFBundleIdentifier', '%s.framework' % get_bundle_id(args))]
elif 'nwjs Helper' in file:
tmp_replaces = [('CFBundleIdentifier', '%s.helper' % get_bundle_id(args))]
else:
error('Cannot patch unknown Info.plist %s' % file)
info(file)
patch_info_plist_file(file, tmp_replaces)
def patch_locales(config, args):
print '\nPatching locales'
locales = config.get('Resources', 'Locales').split(',')
removed_locales = []
generated_locales = []
for infoplist_strings_file in glob(os.path.join(args.output, 'Contents/Resources'), 'InfoPlist.strings'):
locale_dir = os.path.dirname(infoplist_strings_file)
(locale, _) = os.path.splitext(os.path.basename(locale_dir))
if locale not in locales:
removed_locales.append(locale)
shutil.rmtree(locale_dir)
elif config.has_section('Locale %s' % locale):
generated_locales.append(locale)
generate_infoplist_strings_file(infoplist_strings_file, config.items('Locale %s' % locale))
else:
error('Missing [Locale %s] section' % locale)
if len(generated_locales) > 0:
info('Generated locales for %s' % ', '.join(generated_locales))
if len(removed_locales) > 0:
info('Removed locales for %s' % ', '.join(removed_locales))
removed_paks = []
for local_pak in glob(os.path.join(args.output, 'Contents/Versions'), 'locale.pak'):
locale_dir = os.path.dirname(local_pak)
(locale, _) = os.path.splitext(os.path.basename(locale_dir))
if locale != 'en' and locale not in locales:
removed_paks.append(locale)
shutil.rmtree(locale_dir)
if len(removed_paks) > 0:
info('Removed .pak files for %s' % ', '.join(removed_locales))
def patch_icon(config, args):
plist = plistlib.readPlist(os.path.join(args.output, 'Contents/Info.plist'))
icon = os.path.join(os.path.dirname(args.config_file), config.get('Resources', 'Icon'))
dest_icon = os.path.join(args.output, 'Contents/Resources/%s' % plist['CFBundleIconFile'])
info('Copying icon from %s to %s' % (icon, dest_icon))
shutil.copy2(icon, dest_icon)
def codesign_app(config, args):
print '\nCodesigning'
bundleid = get_bundle_id(args)
identity = config.get('Sign', 'ApplicationIdentity')
sandbox = True
if config.has_option('Sign', 'Sandbox'):
sandbox = config.getboolean('Sign', 'Sandbox')
## sign child frameworks and helpers
(_, tmp_child_entitlements) = tempfile.mkstemp()
if config.has_option('Sign', 'ChildEntitlements'):
child = config.get('Sign', 'ChildEntitlements')
child_entitlements = plistlib.readPlist(child)
else:
child_entitlements = {
'com.apple.security.app-sandbox' : sandbox,
'com.apple.security.inherit' : True
}
plistlib.writePlist(child_entitlements, tmp_child_entitlements)
info('Child entitlements: %s' % tmp_child_entitlements)
framework = glob(args.output, 'nwjs Framework.framework', returnOnFound=True)
system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, framework))
helperApp = glob(args.output, 'nwjs Helper.app', returnOnFound=True)
system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, helperApp))
## sign parent app
(_, tmp_parent_entitlements) = tempfile.mkstemp()
if config.has_option('Sign', 'ParentEntitlements'):
parent = config.get('Sign', 'ParentEntitlements')
parent_entitlements = plistlib.readPlist(parent)
else:
parent_entitlements = {}
teamid = get_from_info_plist(args, 'NWTeamID', default=None)
if teamid is None:
groupid = bundleid
else:
groupid = '%s.%s' % (teamid, bundleid)
parent_entitlements['com.apple.security.app-sandbox'] = sandbox
parent_entitlements['com.apple.security.application-groups'] = [groupid]
plistlib.writePlist(parent_entitlements, tmp_parent_entitlements)
info('Parent entitlements: %s' % tmp_parent_entitlements)
system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_parent_entitlements, args.output))
def productbuild(config, args):
print '\nRunning productbuild'
installer_identity = config.get('Package', 'InstallerIdentity')
if config.has_option('Package', 'InstallPath'):
install_path = config.get('Package', 'InstallPath')
else:
install_path = '/Applications'
system('productbuild --component "%s" "%s" --sign "%s" "%s"' % (args.output, install_path, installer_identity, args.pkg))
def main():
parser = argparse.ArgumentParser(description='Signing tool for NW.js app')
parser.add_argument('-C', '--config-file', default='build.cfg', help='config file. (default: build.cfg)')
parser.add_argument('-I', '--input', default='nwjs.app', help='path to input app. (default: nwjs.app)')
parser.add_argument('-O', '--output', default='nwjs_output.app', help='path to output app. (default: nwjs_output.app)')
parser.add_argument('-S', '--skip-patching', default=False, help='run codesign without patching the app. (default: False)', action='store_true')
parser.add_argument('-P', '--pkg', default=None, help='run productbuild to generate .pkg after codesign. (default: None)')
parser.add_argument('-V', '--verbose', default=False, help='display detailed information. (default: False)', action='store_true')
args = parser.parse_args()
global verbose
verbose = args.verbose
# read config file
config = read_config(args)
# make a copy
copy_to_output(args)
if not args.skip_patching:
# patch Info.plist
patch_info_plist(config, args)
# process resources & locales
if config.has_section('Resources'):
if config.has_option('Resources', 'Locales'):
patch_locales(config, args)
if config.has_option('Resources', 'Icon'):
patch_icon(config, args)
# codesign
codesign_app(config, args)
if args.pkg:
productbuild(config, args)
print '\nDone.'
if __name__ == "__main__":
main()

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<string>$GROUPID</string>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>

View file

@ -287,7 +287,10 @@ angular.module('copayApp.controllers').controller('confirmController', function(
tx.amountValueStr = tx.amountStr.split(' ')[0];
tx.amountUnitStr = tx.amountStr.split(' ')[1];
txFormatService.formatAlternativeStr(wallet.coin, tx.toAmount, function(v) {
var parts = v.split(' ');
tx.alternativeAmountStr = v;
tx.alternativeAmountValueStr = parts[0];
tx.alternativeAmountUnitStr = (parts.length > 0) ? parts[1] : '';
});
}
@ -426,6 +429,8 @@ angular.module('copayApp.controllers').controller('confirmController', function(
function showSendMaxWarning(wallet, sendMaxInfo) {
var feeAlternative = '',
msg = '';
function verifyExcludedUtxos() {
var warningMsg = [];
@ -443,9 +448,18 @@ angular.module('copayApp.controllers').controller('confirmController', function(
return warningMsg.join('\n');
};
var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees.", {
fee: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.fee)
});
feeAlternative = txFormatService.formatAlternativeStr(wallet.coin, sendMaxInfo.fee);
if (feeAlternative) {
msg = gettextCatalog.getString("{{feeAlternative}} will be deducted for bitcoin networking fees ({{fee}}).", {
fee: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.fee),
feeAlternative: feeAlternative
});
} else {
msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees).", {
fee: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.fee)
});
}
var warningMsg = verifyExcludedUtxos();
if (!lodash.isEmpty(warningMsg))

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabReceiveController', function($rootScope, $scope, $timeout, $log, $ionicModal, $state, $ionicHistory, $ionicPopover, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError, bitcoinCashJsService, $ionicNavBarDelegate, txFormatService, soundService) {
angular.module('copayApp.controllers').controller('tabReceiveController', function($rootScope, $scope, $timeout, $log, $ionicModal, $state, $ionicHistory, $ionicPopover, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError, bitcoinCashJsService, $ionicNavBarDelegate, txFormatService, soundService, clipboardService) {
var listeners = [];
$scope.bchAddressType = { type: 'cashaddr' };
@ -58,6 +58,12 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
paymentSubscriptionObj.addr = $scope.addr
}
try {
clipboardService.copyToClipboard($scope.wallet.coin == 'bch' && $scope.bchAddressType.type == 'cashaddr' ? 'bitcoincash:' + $scope.addr : $scope.addr);
} catch (error) {
$log.debug("Error copying to clipboard:");
$log.debug(error);
}
// create subscription
var msg = JSON.stringify(paymentSubscriptionObj);
currentAddressSocket.onopen = function (event) {
@ -127,6 +133,12 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
for (var i = 0; i < data.x.out.length; i++) {
if (data.x.out[i].addr == watchAddress) {
$scope.paymentReceivedAmount = txFormatService.formatAmount(data.x.out[i].value, 'full');
$scope.paymentReceivedAlternativeAmount = ''; // For when a subsequent payment is received.
txFormatService.formatAlternativeStr($scope.wallet.coin, data.x.out[i].value, function(alternativeStr){
if (alternativeStr) {
$scope.paymentReceivedAlternativeAmount = alternativeStr;
}
});
}
}
$scope.paymentReceivedCoin = $scope.wallet.coin;
@ -229,6 +241,7 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
});
$scope.$on("$ionicView.enter", function(event, data) {
$scope.showingPaymentReceived = false;
$ionicNavBarDelegate.showBar(true);
});

View file

@ -122,8 +122,11 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
scannerService.openSettings();
};
$scope.reactivationCount = 0;
$scope.attemptToReactivate = function(){
scannerService.reinitialize();
scannerService.reinitialize(function(){
$scope.reactivationCount++;
});
};
$scope.toggleLight = function(){

View file

@ -76,8 +76,11 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
var walletList = [];
lodash.each(walletsToTransfer, function(v) {
var displayBalanceAsFiat =
v.status.alternativeBalanceAvailable &&
config.wallet.settings.priceDisplay === 'fiat';
// BD got v.status as undefined here once during development, just
// after creating a new wallet.
v.status &&
v.status.alternativeBalanceAvailable &&
config.wallet.settings.priceDisplay === 'fiat';
walletList.push({
color: v.color,

View file

@ -0,0 +1,15 @@
angular.module('copayApp')
.config(['$provide', '$logProvider', function($provide, $logProvider) {
// expose a provider to reach debugEnabled in $log
$provide.value('$logProvider', $logProvider);
}])
.decorator('$log', ['$logProvider', '$delegate', function($logProvider, $delegate) {
// override $log.debug to display in Chrome
$delegate.debug = function () {
if ($logProvider.debugEnabled()) {
$delegate.info.apply($delegate, arguments);
}
};
return $delegate;
}]);

View file

@ -1,38 +1,26 @@
'use strict';
angular.module('copayApp.directives')
.directive('copyToClipboard', function(platformInfo, nodeWebkitService, gettextCatalog, ionicToast, clipboard) {
.directive('copyToClipboard', function(clipboardService, ionicToast, gettextCatalog) {
return {
restrict: 'A',
scope: {
copyToClipboard: '=copyToClipboard'
},
link: function(scope, elem, attrs, ctrl) {
var isCordova = platformInfo.isCordova;
var isChromeApp = platformInfo.isChromeApp;
var isNW = platformInfo.isNW;
elem.bind('mouseover', function() {
elem.css('cursor', 'pointer');
});
var msg = gettextCatalog.getString('Copied to clipboard');
elem.bind('click', function() {
var data = scope.copyToClipboard;
if (!data) return;
clipboardService.copyToClipboard(data);
if (isCordova) {
cordova.plugins.clipboard.copy(data);
} else if (isNW) {
nodeWebkitService.writeToClipboard(data);
} else if (clipboard.supported) {
clipboard.copyText(data);
} else {
// No supported
return;
}
scope.$apply(function() {
var msg = gettextCatalog.getString('Copied to clipboard');
scope.$apply(function () {
ionicToast.show(msg, 'bottom', false, 1000);
});
});
}
}

View file

@ -0,0 +1,24 @@
'use strict';
angular.module('copayApp.services').factory('clipboardService', function ($http, $log, platformInfo, nodeWebkitService, gettextCatalog, ionicToast, clipboard) {
var root = {};
root.copyToClipboard = function (data) {
if (!data) return;
$log.debug("Copy '"+data+"' to clipboard");
if (platformInfo.isCordova) {
cordova.plugins.clipboard.copy(data);
} else if (platformInfo.isNW) {
nodeWebkitService.writeToClipboard(data);
} else if (clipboard.supported) {
clipboard.copyText(data);
} else {
// No supported
return;
}
};
return root;
});

View file

@ -107,7 +107,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
enabled: false,
},
soundsEnabled: false,
soundsEnabled: true,
log: {
filter: 'debug',

View file

@ -103,6 +103,7 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
_completeInitialization(status, callback);
});
} else {
isAvailable = true; // XX SP: Availability can change after permissions are granted after being denied.
_completeInitialization(status, callback);
}
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services')
.factory('storageService', function(appConfigService, logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo, secureStorageService, $timeout) {
.factory('storageService', function(appConfigService, logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo, $timeout) {
var root = {};
var storage;
@ -121,11 +121,7 @@ angular.module('copayApp.services')
root.storeProfile = function(profile, cb) {
var profileString = profile.toObj();
if (platformInfo.isNW) {
storage.set('profile', profileString, cb);
} else {
secureStorageService.set('profile', profileString, cb);
}
storage.set('profile', profileString, cb);
};
/**
@ -205,48 +201,19 @@ angular.module('copayApp.services')
* @param {getProfileCallback} cb
*/
root.getProfile = function(cb) {
if (platformInfo.isNW) {
storage.get('profile', function(getErr, getStr) {
_onOldProfileRetrieved(getErr, getStr, cb);
});
return
}
secureStorageService.get('profile', function(secureErr, secureStr) {
var secureProfile;
var oldProfile;
if (secureErr) {
return cb(secureErr, null);
storage.get('profile', function(getErr, getStr) {
if (getErr) {
cb(getErr, null);
return;
}
if (secureStr) {
try {
secureProfile = Profile.fromString(secureStr);
$log.debug('profile: ' + JSON.stringify(secureProfile));
} catch (e) {
$log.error(e);
return cb(e, null);
}
if (!getStr) {
cb(null, null);
return;
}
storage.get('profile', function(getErr, getStr) {
_onOldProfileRetrieved(getErr, getStr, function(oldErr, oldProfile){
if (oldErr) {
return cb(oldErr, null);
}
if (!oldProfile) {
if (secureProfile) {
return cb(null, secureProfile);
} else {
// No profiles found. No errors either.
return cb(null, null);
}
}
_migrateProfiles(oldProfile, secureProfile, cb);
});
});
var profile = Profile.fromString(getStr);
cb(null, profile);
});
};

View file

@ -72,11 +72,19 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
var config = configService.getSync().wallet.settings;
var val = function() {
var v1 = parseFloat((rateService.toFiat(satoshis, config.alternativeIsoCode, coin)).toFixed(2));
v1 = $filter('formatFiatAmount')(v1);
var fiatAmount = rateService.toFiat(satoshis, config.alternativeIsoCode, coin);
var roundedStr = fiatAmount.toFixed(2);
var roundedNum = parseFloat(roundedStr);
var subcent = roundedNum === 0 && fiatAmount > 0;
var lessThanPrefix = '';
if (subcent) {
roundedNum = 0.01;
lessThanPrefix = '< ';
}
var v1 = $filter('formatFiatAmount')(roundedNum);
if (!v1) return null;
return v1 + ' ' + config.alternativeIsoCode;
return lessThanPrefix + v1 + ' ' + config.alternativeIsoCode;
};
// Async version

View file

@ -0,0 +1,68 @@
describe('txFormatService', function(){
var configServiceMock,
rateServiceMock,
txFormatService;
beforeEach(function(){
module('ngLodash');
module('bwcModule');
module('copayApp.filters');
module('copayApp.services');
configServiceMock = {
getSync: jasmine.createSpy()
};
rateServiceMock = {
isAvailable: jasmine.createSpy(),
toFiat: jasmine.createSpy()
};
module(function($provide) {
$provide.value('configService', configServiceMock);
$provide.value('rateService', rateServiceMock);
});
inject(function($injector){
txFormatService = $injector.get('txFormatService');
});
});
it('formatAlternativeStr 0.49 cents.', function() {
configServiceMock.getSync.and.returnValue({
wallet: {
settings: {
alternativeIsoCode: 'USD'
}
}
});
rateServiceMock.isAvailable.and.returnValue(true);
rateServiceMock.toFiat.and.returnValue(0.00499);
var formatted = txFormatService.formatAlternativeStr('bch', 123);
expect(formatted).toBe('< 0.01 USD');
});
it('formatAlternativeStr 0.5 cents.', function() {
configServiceMock.getSync.and.returnValue({
wallet: {
settings: {
alternativeIsoCode: 'USD'
}
}
});
rateServiceMock.isAvailable.and.returnValue(true);
rateServiceMock.toFiat.and.returnValue(0.005);
var formatted = txFormatService.formatAlternativeStr('bch', 123);
expect(formatted).toBe('0.01 USD');
});
});

View file

@ -5,6 +5,7 @@
@import "icons";
@import "buttons";
@import "forms";
@import "qr";
@import "mixins/mixins";
@import "views/views";
@import "directives/directives";

20
src/sass/qr.scss Normal file
View file

@ -0,0 +1,20 @@
qrcode {
&.qr-icon {
&::before {
content: "";
background-size: 100% 100%;
display: block;
margin-left: calc(50% - 22px);
margin-top: 88px;
width: 44px;
height: 44px;
position: absolute;
}
&--bch::before {
background-image: url('../img/qr-overlay-bch.png');
}
&--btc::before {
background-image: url('../img/qr-overlay-btc.png');
}
}
}

View file

@ -36,6 +36,11 @@
.amount-label{
line-height: 30px;
.amount{
font-size: 16px;
color: #9B9B9B;
font-family: "Roboto-Light";
}
.alternative {
font-size: 38px;
margin-bottom: .5rem;
@ -43,11 +48,6 @@
font-family: "Roboto-Light";
}
}
.alternative {
font-size: 16px;
font-family: "Roboto-Light";
color: #9B9B9B;
}
}
}
.item {

11
src/shim/shim.js Normal file
View file

@ -0,0 +1,11 @@
//---------------------------------------------------------------------
//
// Add components what are missing in old JavaScript Engine
//
//---------------------------------------------------------------------
if (!ArrayBuffer['isView']) {
ArrayBuffer.isView = function(a) {
return a !== null && typeof(a) === "object" && a['buffer'] instanceof ArrayBuffer;
};
}

BIN
www/img/qr-overlay-bch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
www/img/qr-overlay-btc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -11,7 +11,7 @@
<link rel="stylesheet" type="text/css" href="css/chartist.css">
<link rel="stylesheet" type="text/css" href="css/bitcoin.com.css">
<link rel="stylesheet" type="text/css" href="css/icomoon.css">
<title>Bitcoin.com - Bitcoin.com Wallet</title>
<title>Bitcoin.com Wallet</title>
<link rel="shortcut icon" href="img/app/favicon.ico">
</head>
<body>

View file

@ -16,8 +16,8 @@
<span translate ng-if="tx.sendMax">Sending maximum amount</span>
</div>
<div class="amount-label">
<div class="alternative">{{tx.alternativeAmountValueStr || '...'}} <span class="unit">{{tx.alternativeAmountUnitStr}}</span></div>
<div class="amount">{{tx.amountValueStr || '...'}} <span class="unit">{{tx.amountUnitStr}}</span></div>
<div class="alternative">{{tx.alternativeAmountStr || '...'}}</div>
</div>
</div>
<div class="info">
@ -77,9 +77,9 @@
</a>
<div class="item item-icon-right" ng-if="wallet" ng-click="chooseFeeLevel(tx, wallet)">
<span class="label">{{'Fee:' | translate}} {{tx.feeLevelName | translate}}</span>
<span class="m10l">{{tx.txp[wallet.id].feeStr || '...'}}</span>
<span class="m10l">{{tx.txp[wallet.id].alternativeFeeStr || '...'}}</span>
<span class="item-note m10l">
<span>{{tx.txp[wallet.id].alternativeFeeStr || '...'}}&nbsp;
<span>{{tx.txp[wallet.id].feeStr || '...'}}&nbsp;
<span class="fee-rate" ng-if="tx.txp[wallet.id].feeRatePerStr"> &middot;
<i class="ion-alert-circled warn" ng-show="tx.txp[wallet.id].feeToHigh"></i> &nbsp;
<span class="fee-rate" ng-class="{'warn':tx.txp[wallet.id].feeToHigh}" translate> {{tx.txp[wallet.id].feeRatePerStr}} of the sending amount </span>

View file

@ -41,16 +41,16 @@
</button>
</span>
<qrcode ng-if="addr" size="220" data="{{ protocolHandler }}:{{addr}}" color="#334"></qrcode>
<qrcode class="qr-icon qr-icon--{{ wallet.coin }}" ng-if="addr" size="220" data="{{ protocolHandler }}:{{addr}}" color="#334"></qrcode>
<div class="address-label">
<span class="ellipsis">{{addr}}</span>
<ion-spinner ng-show="!addr" class="spinner-dark" icon="crescent"></ion-spinner>
</div>
<div>
<button ng-show="addr" class="button-address" ng-click="setAddress(true)">
<span translate>Generate new address</span>
</button><br/>
</div>
</div>
<div>
<button ng-show="addr" class="button-address" ng-click="setAddress(true)">
<span translate>Generate new address</span>
</button><br/>
</div>
</div>
<!-- animation for payment received -->
@ -61,7 +61,8 @@
</svg>
<p class="success animated fadeIn">
<br/>Payment Received!
<span class="payment-received-amount">{{ paymentReceivedAmount }} <span class="payment-received-currency">{{ paymentReceivedCoin }}</span></span>
<span ng-if="!(displayBalanceAsFiat && paymentReceivedAlternativeAmount)" class="payment-received-amount">{{ paymentReceivedAmount }} <span class="payment-received-currency">{{ paymentReceivedCoin }}</span></span>
<span ng-if="displayBalanceAsFiat && paymentReceivedAlternativeAmount" class="payment-received-amount">{{ paymentReceivedAlternativeAmount }}</span></span>
Return To Address<br/>
</p>
</div>

View file

@ -16,7 +16,7 @@
<div class="zero-state-description" translate>You can scan bitcoin addresses, payment requests, paper wallets, and more.</div>
<div class="zero-state-cta">
<div class="ng-hide zero-state-tldr" ng-show="!currentState || currentState === scannerStates.unauthorized" translate>Enable the camera to get started.</div>
<div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.denied" translate>Enable camera access in your device settings to get started.</div>
<div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.denied || reactivationCount > 2" translate>Enable camera access in your device settings to get started.</div>
<div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.unavailable" translate>Please connect a camera to get started.</div>
<button ng-show="!currentState || currentState === scannerStates.unauthorized" class="ng-hide button button-standard button-primary" ng-click="authorize()" translate>Allow Camera Access</button>
<button ng-show="currentState === scannerStates.denied && canOpenSettings" class="ng-hide button button-standard button-primary" ng-click="openSettings()" translate>Open Settings</button>