Merge pull request #203 from Bitcoin-com/wallet/sprint/18

Wallet/sprint/18
This commit is contained in:
Jean-Baptiste Dominguez 2018-07-03 12:28:25 +09:00 committed by GitHub
commit 4a00e3594d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 4771 additions and 403 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']);
@ -297,6 +348,23 @@ module.exports = function(grunt) {
grunt.registerTask('android-debug', ['exec:androiddebug', 'exec:androidrun']);
grunt.registerTask('android', ['exec:android']);
grunt.registerTask('android-release', ['prod', 'exec:android', 'exec:androidsign']);
grunt.registerTask('desktopsign', ['exec:desktopsign', 'exec:desktopverify']);
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

@ -72,8 +72,9 @@
<plugin name="cordova-plugin-queries-schemes" spec="~0.1.5" />
<plugin name="cordova-plugin-firebase" spec="https://github.com/arnesson/cordova-plugin-firebase.git" />
<plugin name="cordova-plugin-wkwebview-inputfocusfix" spec="https://github.com/onderceylan/cordova-plugin-wkwebview-inputfocusfix.git" />
<!-- Changes in error descriptions may break the use of cordova-plugin-secure-storage -->
<plugin name="cordova-plugin-secure-storage" spec="2.6.8" />
<plugin name="cordova-plugin-media" spec="~5.0.2">
<variable name="KEEP_AVAUDIOSESSION_ALWAYS_ACTIVE" value="NO" />
</plugin>
<!-- 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",
@ -85,6 +89,7 @@
"scripts": {
"postinstall": "bower install",
"start": "npm run build:www && ionic serve --nolivereload --nogulp -s --address 0.0.0.0",
"start:chrome": "npm run build:www && ionic serve --nolivereload --nogulp -s --address 0.0.0.0 --browser \"google chrome\"",
"start:ios": "npm run build:www && npm run build:ios && npm run open:ios",
"start:android": "npm run build:www && npm run build:android && npm run run:android",
"start:windows": "npm run build:www && npm run build:windows",
@ -98,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"

3634
i18n/po/ca/template-ca.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Czech\n"
"Language: cs\n"
"PO-Revision-Date: 2018-05-08 00:44-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:46+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -989,7 +989,7 @@ msgstr "Povolit push notifikace"
#: www/views/preferencesNotifications.html:33
msgid "Enable sound"
msgstr ""
msgstr "Povolit zvuky"
#: www/views/tab-scan.html:18
msgid "Enable the camera to get started."
@ -1342,7 +1342,7 @@ msgstr "Začněte"
#: www/views/addressbook.html:20
msgid "Get started by adding your first one."
msgstr "Začněte přidáním své první."
msgstr "Začněte přidáním prvního."
#: src/js/services/onGoingProcess.js:23
msgid "Getting fee levels..."
@ -1582,7 +1582,7 @@ msgstr "Nesprávná síťová adresa"
#: src/js/controllers/confirm.js:306
#: src/js/services/bwcError.js:44
msgid "Insufficient confirmed funds"
msgstr ""
msgstr "Nedostatečné potvrzené prostředky"
#: src/js/controllers/topup.js:165
#: src/js/controllers/topup.js:177
@ -1709,7 +1709,7 @@ msgstr "Načítání informací o transakci..."
#: www/views/tab-settings.html:100
msgid "Lock App"
msgstr "Uzamknout aplikaci"
msgstr "Uzamknutí aplikace"
#: src/js/controllers/lockSetup.js:23
msgid "Lock by Fingerprint"
@ -2049,7 +2049,7 @@ msgstr "Otevřít GitHub projekt"
#: src/js/controllers/bitpayCard.js:123
#: src/js/controllers/tx-details.js:192
msgid "Open Explorer"
msgstr ""
msgstr "Otevřít Explorer"
#: www/views/tab-scan.html:22
msgid "Open Settings"
@ -2256,7 +2256,7 @@ msgstr "Stiskněte znovu pro ukončení"
#: src/js/services/feeService.js:11
msgid "Priority"
msgstr "Priorita"
msgstr "Priorit"
#: www/views/includes/incomingDataMenu.html:80
msgid "Private Key"
@ -3196,7 +3196,7 @@ msgstr "Zobrazit Podmínky použití"
#: src/js/controllers/bitpayCard.js:122
#: src/js/controllers/tx-details.js:191
msgid "View Transaction on Explorer.Bitcoin.com"
msgstr ""
msgstr "Zobrazit transakci na Explorer.Bitcoin.com"
#: src/js/controllers/tab-home.js:148
msgid "View Update"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: German\n"
"Language: de\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:49+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -32,7 +32,7 @@ msgstr "- {{btx.feeRateStr}}ของธุรกรรม"
#: www/views/modals/txp-details.html:102
msgid "- {{tx.feeRateStr}} of the transaction"
msgstr ""
msgstr "- {{tx.feeRateStr}} der Transaktion"
#: www/views/feedback/rateApp.html:7
msgid "5-star ratings help us get {{appName}} into more hands, and more users means more resources can be committed to the app!"
@ -41,7 +41,7 @@ msgstr "Eine 5-Sterne Bewertung hilft uns, {{appName}} unter die Leute zu bringe
#: www/views/mercadoLibre.html:18
#: www/views/mercadoLibre.html:40
msgid "<b>Only</b> redeemable on Mercado Livre (Brazil)"
msgstr ""
msgstr "<b>Nur</b> einlösbar auf Mercado Livre (Brasilien)"
#: src/js/controllers/feedback/send.js:27
#: www/views/feedback/complete.html:21
@ -124,7 +124,7 @@ msgstr "Ein optionales Passwort zur Sicherung der Wiederherstellungsphrase hinzu
#: www/views/includes/incomingDataMenu.html:41
msgid "Add as a contact"
msgstr ""
msgstr "Als Kontakt hinzufügen"
#: src/js/controllers/confirm.js:424
msgid "Add description"
@ -132,11 +132,11 @@ msgstr "Beschreibung hinzufügen"
#: www/views/topup.html:6
msgid "Add funds"
msgstr ""
msgstr "Guthaben aufladen"
#: src/js/services/bitpayAccountService.js:78
msgid "Add this BitPay account ({{email}})?"
msgstr ""
msgstr "Dieses BitPay Konto hinzufügen ({{email}})?"
#: www/views/add.html:3
msgid "Add wallet"
@ -204,7 +204,7 @@ msgstr "Alternative Währung"
#: src/js/controllers/buyAmazon.js:98
msgid "Amazon.com is not available at this moment. Please try back later."
msgstr ""
msgstr "Amazon.com ist zurzeit nicht verfügbar. Bitte versuchen Sie es später nochmal."
#: www/views/amount.html:44
#: www/views/customAmount.html:34
@ -222,7 +222,7 @@ msgstr "Betrag zu hoch"
#: www/views/includes/walletHistory.html:31
msgid "Amount too low to spend"
msgstr ""
msgstr "Betrag ist zu niedrig zum ausgeben"
#: src/js/controllers/tab-home.js:147
msgid "An update to this app is available. For your security, please update to the latest version."
@ -359,12 +359,12 @@ msgstr "Bitcoin-Adresse"
#: www/views/cashScan.html:4
msgid "Bitcoin Cash (BCH) Balances"
msgstr ""
msgstr "Bitcoin Cash (BCH) Guthaben"
#: www/views/preferencesCash.html:3
#: www/views/tab-settings.html:47
msgid "Bitcoin Cash Support"
msgstr ""
msgstr "Bitcoin Cash Support"
#: www/views/tab-home.html:98
#: www/views/tab-settings.html:115
@ -380,11 +380,11 @@ msgstr "Bitcoin-Netzwerk Gebührenübersicht"
#: www/views/tab-home.html:83
#: www/views/tab-settings.html:107
msgid "Bitcoin Core Wallets"
msgstr ""
msgstr "Bitcoin Core Brieftaschen"
#: src/js/services/incomingData.js:151
msgid "Bitcoin cash Payment"
msgstr ""
msgstr "Bitcoin Cash Zahlung"
#: www/views/onboarding/tour.html:31
msgid "Bitcoin is a currency."
@ -404,7 +404,7 @@ msgstr "Bitcoin Transaktionen enthalten eine Gebühr für die \"Miners\" im Netz
#: www/views/buyAmazon.html:108
msgid "Bought {{amountUnitStr}}"
msgstr ""
msgstr "{{amountUnitStr}} erworben"
#: www/views/modals/txp-details.html:36
msgid "Broadcast Payment"
@ -474,7 +474,7 @@ msgstr "Abbruch"
#: www/views/copayers.html:36
msgid "Cancel invitation"
msgstr ""
msgstr "Einladung zurückziehen"
#: src/js/controllers/onboarding/tour.js:52
msgid "Cannot Create Wallet"
@ -519,7 +519,7 @@ msgstr "Cache leeren"
#: src/js/controllers/confirm.js:373
#: src/js/controllers/modals/txpDetails.js:49
msgid "Click to accept"
msgstr ""
msgstr "Klicken Sie zum Akzeptieren"
#: src/js/controllers/confirm.js:367
msgid "Click to pay"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Spanish\n"
"Language: es\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:57+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Persian\n"
"Language: fa\n"
"PO-Revision-Date: 2018-05-08 00:44-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:53+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -989,7 +989,7 @@ msgstr "فعال کردن اعلان های با فرمت push"
#: www/views/preferencesNotifications.html:33
msgid "Enable sound"
msgstr ""
msgstr "فعال کردن صدا"
#: www/views/tab-scan.html:18
msgid "Enable the camera to get started."
@ -1582,7 +1582,7 @@ msgstr "آدرس شبکه نادرست"
#: src/js/controllers/confirm.js:306
#: src/js/services/bwcError.js:44
msgid "Insufficient confirmed funds"
msgstr ""
msgstr "موجودی تائید شذه ناکافی"
#: src/js/controllers/topup.js:165
#: src/js/controllers/topup.js:177
@ -2049,7 +2049,7 @@ msgstr "باز کردن پروژه GitHub"
#: src/js/controllers/bitpayCard.js:123
#: src/js/controllers/tx-details.js:192
msgid "Open Explorer"
msgstr ""
msgstr "باز کردن مرورگر"
#: www/views/tab-scan.html:22
msgid "Open Settings"
@ -3196,7 +3196,7 @@ msgstr "مشاهده شرایط و ضوابط خدمات"
#: src/js/controllers/bitpayCard.js:122
#: src/js/controllers/tx-details.js:191
msgid "View Transaction on Explorer.Bitcoin.com"
msgstr ""
msgstr "مشاهده تراکنش در Explorer.Bitcoin.com"
#: src/js/controllers/tab-home.js:148
msgid "View Update"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: French\n"
"Language: fr\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:48+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Italian\n"
"Language: it\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:50+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Japanese\n"
"Language: ja\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:51+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Korean\n"
"Language: ko\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:52+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Dutch\n"
"Language: nl\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:48+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Polish\n"
"Language: pl\n"
"PO-Revision-Date: 2018-05-08 00:44-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:54+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:55+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Swedish\n"
"Language: sv\n"
"PO-Revision-Date: 2018-05-08 00:44-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:58+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -187,11 +187,11 @@ msgstr ""
#: www/views/tab-scan.html:21
msgid "Allow Camera Access"
msgstr ""
msgstr "Tillåt kameraåtkomst"
#: www/views/onboarding/notifications.html:7
msgid "Allow notifications"
msgstr ""
msgstr "Tillåt notificationer"
#: www/views/onboarding/disclaimer.html:14
msgid "Almost done! Let's review."
@ -200,149 +200,149 @@ msgstr ""
#: www/views/preferencesAltCurrency.html:4
#: www/views/tab-settings.html:79
msgid "Alternative Currency"
msgstr ""
msgstr "Alternativ Valuta"
#: src/js/controllers/buyAmazon.js:98
msgid "Amazon.com is not available at this moment. Please try back later."
msgstr ""
msgstr "Amazon.com är inte tillgängligt för tillfället. Försök igen senare."
#: www/views/amount.html:44
#: www/views/customAmount.html:34
#: www/views/includes/output.html:7
msgid "Amount"
msgstr ""
msgstr "Belopp"
#: src/js/services/bwcError.js:110
msgid "Amount below minimum allowed"
msgstr ""
msgstr "Belopp under minsta tillåtna"
#: src/js/controllers/confirm.js:216
msgid "Amount too big"
msgstr ""
msgstr "Beloppet för stort"
#: www/views/includes/walletHistory.html:31
msgid "Amount too low to spend"
msgstr ""
msgstr "Beloppet för lågt för att spendera"
#: src/js/controllers/tab-home.js:147
msgid "An update to this app is available. For your security, please update to the latest version."
msgstr ""
msgstr "En uppdatering för appen är tillgänglig. För din säkerhet, var vänlig uppdatera till den senaste versionen."
#: www/views/backupWarning.html:14
msgid "Anyone with your backup phrase can access or spend your bitcoin."
msgstr ""
msgstr "Vem som helst med din återhämtnings fras kan kommat åt eller spendera dina bitcoin."
#: www/views/addresses.html:94
msgid "Approximate Bitcoin network fee to transfer wallet's balance (with normal priority)"
msgstr ""
msgstr "Ungefärlig Bitcoin nätverks avgift för att överföra plånbokens saldo (med normal prioritet)"
#: www/views/backupWarning.html:10
msgid "Are you being watched?"
msgstr ""
msgstr "Håller någon ögonen på dig?"
#: src/js/controllers/preferencesExternal.js:15
msgid "Are you being watched? Anyone with your recovery phrase can access or spend your bitcoin."
msgstr ""
msgstr "Håller någon ögonen på dig? Vem som helst med din återhämtnings fras kan kommat åt eller spendera dina bitcoin."
#: src/js/controllers/copayers.js:56
msgid "Are you sure you want to cancel and delete this wallet?"
msgstr ""
msgstr "Är du säker på att du vill avbryta och ta bort denna plånboken?"
#: src/js/controllers/addressbookView.js:37
msgid "Are you sure you want to delete this contact?"
msgstr ""
msgstr "Är du säker på att du vill ta bort denna kontakten?"
#: src/js/controllers/preferencesDelete.js:25
msgid "Are you sure you want to delete this wallet?"
msgstr ""
msgstr "Är du säker på att du vill ta bort denna plånboken?"
#: src/js/controllers/modals/txpDetails.js:154
msgid "Are you sure you want to reject this transaction?"
msgstr ""
msgstr "Är du säker på att du vill avvisa denna transaktion?"
#: src/js/controllers/modals/txpDetails.js:171
msgid "Are you sure you want to remove this transaction?"
msgstr ""
msgstr "Är du säker på att du vill ta bort denna transaktion?"
#: src/js/controllers/onboarding/backupRequest.js:23
msgid "Are you sure you want to skip it?"
msgstr ""
msgstr "Är du säker på att du vill hoppa över detta?"
#: www/views/modals/bitpay-card-confirmation.html:4
msgid "Are you sure you would like to log out of your BitPay Card account?"
msgstr ""
msgstr "Är du säker på att du vill logga ut från ditt BitPay Card konto?"
#: src/js/controllers/preferencesBitpayCard.js:7
#: src/js/controllers/preferencesBitpayServices.js:20
msgid "Are you sure you would like to remove your BitPay Card ({{lastFourDigits}}) from this device?"
msgstr ""
msgstr "Är du säker på att du vill ta bort ditt BitPay Card ({{lastFourDigits}}) från denna enheten?"
#: www/views/includes/walletInfo.html:10
msgid "Auditable"
msgstr ""
msgstr "Granskningsbar"
#: www/views/modals/wallet-balance.html:42
msgid "Available"
msgstr ""
msgstr "Tillgänglig"
#: www/views/includes/available-balance.html:3
msgid "Available Balance"
msgstr ""
msgstr "Tillgängligt Saldo"
#: www/views/modals/chooseFeeLevel.html:24
#: www/views/preferencesFee.html:15
msgid "Average confirmation time"
msgstr ""
msgstr "Genomsnittlig bekräftelsetid"
#: www/views/join.html:143
#: www/views/tab-create-personal.html:113
#: www/views/tab-create-shared.html:142
#: www/views/tab-import-phrase.html:51
msgid "BIP32 path for address derivation"
msgstr ""
msgstr "BIP32 sökväg för adress derivering"
#: www/views/cashScan.html:25
msgid "BTC wallets"
msgstr ""
msgstr "BTC plånböcker"
#: www/views/preferences.html:34
msgid "Backup"
msgstr ""
msgstr "Säkerhetskopiera"
#: www/views/includes/backupNeededPopup.html:7
msgid "Backup Needed"
msgstr ""
msgstr "Säkerhetskopiering Behövs"
#: src/js/controllers/lockSetup.js:87
msgid "Backup all livenet wallets before using this function"
msgstr ""
msgstr "Säkerhetskopiera alla livenet plånböcker innan du använder denna funktion"
#: src/js/controllers/cashScan.js:64
#: www/views/includes/walletListSettings.html:12
#: www/views/preferences.html:36
msgid "Backup needed"
msgstr ""
msgstr "Säkerhetskopiering behövs"
#: www/views/includes/backupNeededPopup.html:9
msgid "Backup now"
msgstr ""
msgstr "Säkerhetskopiera nu"
#: www/views/onboarding/backupRequest.html:11
#: www/views/tab-export-file.html:89
msgid "Backup wallet"
msgstr ""
msgstr "Säkerhetskopiera plånbok"
#: src/js/controllers/lockSetup.js:84
msgid "Backup your wallet before using this function"
msgstr ""
msgstr "Säkerhetskopiera din plånbok innan du använder denna funktion"
#: src/js/services/profileService.js:446
msgid "Bad wallet invitation"
msgstr ""
msgstr "Fel på plånboks inbjudan"
#: www/views/preferencesInformation.html:102
msgid "Balance By Address"
msgstr ""
msgstr "Saldo av Address"
#: www/views/includes/confirmBackupPopup.html:7
msgid "Be sure to store your recovery phrase in a secure place. If this app is deleted, your money cannot be recovered without it."

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Vietnamese\n"
"Language: vi\n"
"PO-Revision-Date: 2018-05-15 20:18-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:59+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"
@ -24,7 +24,7 @@ msgstr "(có thể chi tiêu gấp đôi)"
#: www/views/modals/txp-details.html:159
msgid "* A payment proposal can be deleted if 1) you are the creator, and no other copayer has signed, or 2) 24 hours have passed since the proposal was created."
msgstr ""
msgstr "* A payment proposal can be deleted if 1) you are the creator, and no other copayer has signed, or 2) 24 hours have passed since the proposal was created."
#: www/views/tx-details.html:82
msgid "- {{btx.feeRateStr}} of the transaction"
@ -41,16 +41,16 @@ msgstr "Xếp hạng 5 sao giúp chúng tôi để {{appName}} đến tay nhiề
#: www/views/mercadoLibre.html:18
#: www/views/mercadoLibre.html:40
msgid "<b>Only</b> redeemable on Mercado Livre (Brazil)"
msgstr ""
msgstr "<b>Only</b> redeemable on Mercado Livre (Brazil)"
#: src/js/controllers/feedback/send.js:27
#: www/views/feedback/complete.html:21
msgid "A member of the team will review your feedback as soon as possible."
msgstr ""
msgstr "A member of the team will review your feedback as soon as possible."
#: src/js/controllers/confirm.js:401
msgid "A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded."
msgstr ""
msgstr "A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded."
#: src/js/controllers/confirm.js:395
msgid "A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided."

View file

@ -11,7 +11,7 @@ msgstr ""
"Last-Translator: emilold\n"
"Language-Team: Chinese Simplified\n"
"Language: zh\n"
"PO-Revision-Date: 2018-05-08 00:44-0400\n"
"PO-Revision-Date: 2018-06-22T04:02:44+0000\n"
#: www/views/modals/paypro.html:34
msgid "(Trusted)"

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

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, $stateParams, $window, $state, $log, profileService, bitcore, bitcoreCash, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification, externalLinkService, firebaseEventsService) {
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, $stateParams, $window, $state, $log, profileService, bitcore, bitcoreCash, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification, externalLinkService, firebaseEventsService, soundService) {
var countDown = null;
var FEE_TOO_HIGH_LIMIT_PER = 15;
@ -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))
@ -624,10 +638,11 @@ angular.module('copayApp.controllers').controller('confirmController', function(
(processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal())
) && !isOn) {
$scope.sendStatus = 'success';
if (config.soundsEnabled && $scope.wallet.coin == 'bch') {
var audio = new Audio('misc/bch_sent.mp3');
audio.play();
if ($state.current.name === "tabs.send.confirm") { // XX SP: Otherwise all open wallets on other devices play this sound if you have been in a send flow before on that device.
soundService.play('misc/payment_sent.mp3');
}
firebaseEventsService.logEvent('sent_bitcoin', { coin: $scope.wallet.coin });
$timeout(function() {
$scope.$digest();

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) {
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' };
@ -13,6 +13,8 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
var currentAddressSocket = {};
var paymentSubscriptionObj = { op:"addr_sub" }
var config;
$scope.displayBalanceAsFiat = true;
$scope.requestSpecificAmount = function() {
@ -56,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) {
@ -125,9 +133,20 @@ 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;
if ($state.current.name === "tabs.receive") {
soundService.play('misc/payment_received.mp3');
}
$scope.$apply(function () {
$scope.showingPaymentReceived = true;
});
@ -215,12 +234,14 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
})
];
configService.whenAvailable(function(config) {
$scope.displayBalanceAsFiat = config.wallet.settings.priceDisplay === 'fiat';
configService.whenAvailable(function(_config) {
$scope.displayBalanceAsFiat = _config.wallet.settings.priceDisplay === 'fiat';
config = _config;
});
});
$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

@ -1383,6 +1383,11 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
if (document.body.classList.contains('keyboard-open')) {
document.body.classList.remove('keyboard-open');
$log.debug('Prevented keyboard open bug..');
}
$log.debug('Route change from:', fromState.name || '-', ' to:', toState.name);
$log.debug(' toParams:' + JSON.stringify(toParams || {}));
$log.debug(' fromParams:' + JSON.stringify(fromParams || {}));

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

@ -0,0 +1,44 @@
'use strict';
angular.module('copayApp.services').service('soundService', function($log, $timeout, platformInfo, configService) {
var root = {};
/**
* Play a sound (when enabled in the configuration) using the Cordova Media-plugin (on Mobile) or html5-audio (on Desktop) relative to the www-root
* Make sure there is a .ogg file as well for NW.js (desktop) implementation
* @param soundFile
*/
root.play = function(soundFile) {
configService.whenAvailable(function(config) {
if (config.soundsEnabled) {
if (platformInfo.isCordova) {
if (platformInfo.isAndroid) {
var p = window.location.pathname;
var device_path = p.substring(0, p.lastIndexOf('/'));
soundFile = device_path + '/' + soundFile;
}
var audio = new Media(soundFile,
function () {
$log.debug("playAudio(bch_sent):Audio Success");
},
function (err) {
$log.debug("playAudio():Audio Error: " + err);
}
);
audio.play({playAudioWhenScreenIsLocked: false}); // XX SP: "Locked" is also the mute switch in iOS
} else {
if (platformInfo.isNW) {
soundFile = soundFile.substring(0, soundFile.lastIndexOf('.')) + ".ogg";
$log.debug("Playing .ogg file ("+soundFile+"), as NW.js has no mp3 support");
}
new Audio(soundFile).play();
}
}
});
};
return root;
});

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

@ -10,6 +10,10 @@ angular.module('copayApp.services')
isoCode: 'en',
rateCode: 'USD'
}, {
name: 'català',
isoCode: 'ca',
rateCode: 'EUR'
},{
name: 'Čeština',
isoCode: 'cs',
rateCode: 'EUR'
@ -55,6 +59,10 @@ angular.module('copayApp.services')
name: 'Português',
isoCode: 'pt',
rateCode: 'EUR'
}, {
name: 'русский язык',
isoCode: 'ru',
rateCode: 'RUB'
}, {
name: '한국어',
isoCode: 'ko',

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

@ -86,6 +86,9 @@ slide-to-accept-success {
transition: transform $duration ease, opacity $duration ease;
transition-delay: 250ms;
margin-bottom: constant(safe-area-inset-bottom); /* iOS 11.0 */
margin-bottom: env(safe-area-inset-bottom); /* iOS 11.2 */
&.reveal {
-webkit-transform: translateY(0);
transform: translateY(0);

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 {

View file

@ -12,6 +12,11 @@ wallet-selector {
font-weight: bold;
padding-bottom: 10px;
border-bottom: 1px solid #EFEFEF;
.subtitle {
color: $v-mid-gray;
font-size: $font-size-small;
font-weight: 300;
}
.wallet-coin-logo {
vertical-align: middle;
margin-right: 5px;

View file

@ -43,6 +43,11 @@
.icon {
color: $v-mid-gray;
}
.subtitle {
color: $v-mid-gray;
font-size: $font-size-small;
font-weight: 300;
}
}
}
}
}

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>

Binary file not shown.

Binary file not shown.

BIN
www/misc/payment_sent.ogg Normal file

Binary file not shown.

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

@ -8,8 +8,8 @@
<div class="header" ng-if="!walletsBtc[0] || !walletsBch[0]">{{title}}</div>
<div class="subheader" ng-if="walletsBch[0] && walletsBtc[0]">
<img class="wallet-coin-logo" src="img/bitcoin-cash-logo.svg" width="22">
<span translate>Bitcoin Cash (BCH)</span>
<div translate>Bitcoin Cash (BCH)</div>
<div translate class="subtitle">Instant transactions with low fees</div>
</div>
<a
ng-repeat="wallet in walletsBch track by $index"
@ -40,8 +40,8 @@
</a>
<div class="subheader" ng-if="walletsBtc[0] && walletsBch[0]" translate>
<img class="wallet-coin-logo" src="img/icon-bitcoin.svg" width="18">
<span translate>Bitcoin Core (BTC)</span>
<div translate>Bitcoin Core (BTC)</div>
<div translate class="subtitle">Slow transactions with high fees</div>
</div>
<a
ng-repeat="wallet in walletsBtc track by $index"

View file

@ -57,8 +57,8 @@
<div class="list card">
<div class="item item-icon-right item-heading">
<img class="wallet-coin-logo" src="img/bitcoin-cash-logo.svg" width="22">
<span translate>Bitcoin Cash (BCH)</span>
<div translate>Bitcoin Cash (BCH)</div>
<div translate class="subtitle">Instant transactions with low fees</div>
<a ui-sref="tabs.add({coin:'bch'})"><i class="icon ion-ios-plus-empty list-add-button"></i></a>
</div>
<div>
@ -72,8 +72,8 @@
<div class="list card">
<div class="item item-icon-right item-heading">
<img class="wallet-coin-logo" src="img/icon-bitcoin.svg" width="18">
<span translate>Bitcoin Core (BTC)</span>
<div translate>Bitcoin Core (BTC)</div>
<div translate class="subtitle">Slow transactions with high fees</div>
<a ui-sref="tabs.add"><i class="icon ion-ios-plus-empty list-add-button"></i></a>
</div>
<div>

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>