Browse Source

ADD: Apple Watch support

feesheet
Marcos Rodriguez Vélez 6 years ago
committed by Overtorment
parent
commit
33bffaa7ab
  1. 2
      .flowconfig
  2. 4
      .gitignore
  3. 3
      App.js
  4. 22
      App.test.js
  5. 2
      BlueApp.js
  6. 61
      BlueComponents.js
  7. 8
      BlueElectrum.js
  8. 5
      Electrum.test.js
  9. 2
      HDWallet.test.js
  10. 138
      WatchConnectivity.js
  11. 1
      __mocks__/@react-native-community/async-storage.js
  12. 151
      android/app/app.iml
  13. 3
      android/app/build.gradle
  14. BIN
      android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf
  15. BIN
      android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf
  16. BIN
      android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf
  17. BIN
      android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
  18. BIN
      android/app/src/main/assets/fonts/Octicons.ttf
  19. 2
      android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java
  20. 2
      android/settings.gradle
  21. 5
      class/abstract-hd-wallet.js
  22. 2
      class/abstract-wallet.js
  23. 15
      class/app-storage.js
  24. 3
      class/legacy-wallet.js
  25. 3
      class/lightning-custodian-wallet.js
  26. 2
      currency.js
  27. 2161
      ios/BlueWallet.xcodeproj/project.pbxproj
  28. 2
      ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWallet-tvOS.xcscheme
  29. 32
      ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWallet.xcscheme
  30. 131
      ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWalletWatch (Notification).xcscheme
  31. 128
      ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWalletWatch.xcscheme
  32. 47
      ios/BlueWallet.xcodeproj/xcuserdata/marcosrodriguez.xcuserdatad/xcschemes/xcschememanagement.plist
  33. 19
      ios/BlueWallet.xcworkspace/contents.xcworkspacedata
  34. 8
      ios/BlueWallet.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  35. 6
      ios/BlueWallet/AppDelegate.h
  36. 20
      ios/BlueWallet/AppDelegate.m
  37. 2
      ios/BlueWallet/Info.plist
  38. 56
      ios/BlueWalletWatch Extension/ExtensionDelegate.swift
  39. 38
      ios/BlueWalletWatch Extension/Info.plist
  40. 57
      ios/BlueWalletWatch Extension/InterfaceController.swift
  41. 38
      ios/BlueWalletWatch Extension/NotificationController.swift
  42. 155
      ios/BlueWalletWatch Extension/NumericKeypadInterfaceController.swift
  43. 39
      ios/BlueWalletWatch Extension/Objects/Transaction.swift
  44. 52
      ios/BlueWalletWatch Extension/Objects/TransactionTableRow.swift
  45. 51
      ios/BlueWalletWatch Extension/Objects/Wallet.swift
  46. 32
      ios/BlueWalletWatch Extension/Objects/WalletGradient.swift
  47. 36
      ios/BlueWalletWatch Extension/Objects/WalletInformation.swift
  48. 103
      ios/BlueWalletWatch Extension/Objects/WatchDataSource.swift
  49. 20
      ios/BlueWalletWatch Extension/PushNotificationPayload.apns
  50. 115
      ios/BlueWalletWatch Extension/ReceiveInterfaceController.swift
  51. 90
      ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift
  52. 68
      ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift
  53. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/1024.png
  54. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/58.png
  55. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/87.png
  56. 92
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Contents.json
  57. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-172.png
  58. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-173.png
  59. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-196.png
  60. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-48.png
  61. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-55.png
  62. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-88.png
  63. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/group-copy-2@3x.png
  64. BIN
      ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/watch.png
  65. 6
      ios/BlueWalletWatch/Assets.xcassets/Contents.json
  66. 13
      ios/BlueWalletWatch/Assets.xcassets/loadingIndicator.imageset/Contents.json
  67. BIN
      ios/BlueWalletWatch/Assets.xcassets/loadingIndicator.imageset/group-copy-2@3x.png
  68. 13
      ios/BlueWalletWatch/Assets.xcassets/pendingConfirmation.imageset/Contents.json
  69. BIN
      ios/BlueWalletWatch/Assets.xcassets/pendingConfirmation.imageset/shape@3x.png
  70. 13
      ios/BlueWalletWatch/Assets.xcassets/qr-code.imageset/Contents.json
  71. BIN
      ios/BlueWalletWatch/Assets.xcassets/qr-code.imageset/qr-code@3x.png
  72. 13
      ios/BlueWalletWatch/Assets.xcassets/receivedArrow.imageset/Contents.json
  73. BIN
      ios/BlueWalletWatch/Assets.xcassets/receivedArrow.imageset/path-copy-3@2x.png
  74. 13
      ios/BlueWalletWatch/Assets.xcassets/sentArrow.imageset/Contents.json
  75. BIN
      ios/BlueWalletWatch/Assets.xcassets/sentArrow.imageset/path-copy@2x.png
  76. 23
      ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/Contents.json
  77. BIN
      ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask.png
  78. BIN
      ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask@2x.png
  79. BIN
      ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask@3x.png
  80. 23
      ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/Contents.json
  81. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask.png
  82. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@2x.png
  83. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@3x.png
  84. 13
      ios/BlueWalletWatch/Assets.xcassets/walletHD.imageset/Contents.json
  85. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletHD.imageset/mask@3x.png
  86. 13
      ios/BlueWalletWatch/Assets.xcassets/walletLightningCustodial.imageset/Contents.json
  87. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletLightningCustodial.imageset/mask@3x.png
  88. 23
      ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/Contents.json
  89. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask.png
  90. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask@2x.png
  91. BIN
      ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask@3x.png
  92. 339
      ios/BlueWalletWatch/Base.lproj/Interface.storyboard
  93. 33
      ios/BlueWalletWatch/Info.plist
  94. 454
      ios/KeychainSwiftDistrib.swift
  95. 127
      ios/Podfile
  96. 232
      ios/Podfile.lock
  97. 26
      ios/Pods/Local Podspecs/RNDeviceInfo.podspec.json
  98. 23
      ios/Pods/Local Podspecs/RNSVG.podspec.json
  99. 565
      ios/Pods/Local Podspecs/React.podspec.json
  100. 97
      ios/Pods/Local Podspecs/react-native-camera.podspec.json

2
.flowconfig

@ -67,4 +67,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[version] [version]
^0.86.0 ^0.97.0

4
.gitignore

@ -57,4 +57,6 @@ buck-out/
#BlueWallet #BlueWallet
release-notes.json release-notes.json
release-notes.txt release-notes.txt
ios/Pods/

3
App.js

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Linking, AppState, Clipboard, StyleSheet, KeyboardAvoidingView, Platform, View, AsyncStorage } from 'react-native'; import { Linking, AppState, Clipboard, StyleSheet, KeyboardAvoidingView, Platform, View } from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
import { NavigationActions } from 'react-navigation'; import { NavigationActions } from 'react-navigation';
import MainBottomTabs from './MainBottomTabs'; import MainBottomTabs from './MainBottomTabs';

22
App.test.js

@ -5,13 +5,11 @@ import TestRenderer from 'react-test-renderer';
import Settings from './screen/settings/settings'; import Settings from './screen/settings/settings';
import Selftest from './screen/selftest'; import Selftest from './screen/selftest';
import { BlueHeader } from './BlueComponents'; import { BlueHeader } from './BlueComponents';
import MockStorage from './MockStorage';
import { FiatUnit } from './models/fiatUnit'; import { FiatUnit } from './models/fiatUnit';
import AsyncStorage from '@react-native-community/async-storage';
global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment
let assert = require('assert'); let assert = require('assert');
jest.mock('react-native-qrcode-svg', () => 'Video'); jest.mock('react-native-qrcode-svg', () => 'Video');
const AsyncStorage = new MockStorage();
jest.setMock('AsyncStorage', AsyncStorage);
jest.useFakeTimers(); jest.useFakeTimers();
jest.mock('Picker', () => { jest.mock('Picker', () => {
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
@ -105,7 +103,6 @@ it('Selftest work', () => {
}); });
it('Appstorage - loadFromDisk works', async () => { it('Appstorage - loadFromDisk works', async () => {
AsyncStorage.storageCache = {}; // cleanup from other tests
/** @type {AppStorage} */ /** @type {AppStorage} */
let Storage = new AppStorage(); let Storage = new AppStorage();
let w = new SegwitP2SHWallet(); let w = new SegwitP2SHWallet();
@ -125,16 +122,14 @@ it('Appstorage - loadFromDisk works', async () => {
// emulating encrypted storage (and testing flag) // emulating encrypted storage (and testing flag)
AsyncStorage.storageCache.data = false; await AsyncStorage.setItem('data', false);
AsyncStorage.storageCache.data_encrypted = '1'; // flag await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, '1');
let Storage3 = new AppStorage(); let Storage3 = new AppStorage();
isEncrypted = await Storage3.storageIsEncrypted(); isEncrypted = await Storage3.storageIsEncrypted();
assert.ok(isEncrypted); assert.ok(isEncrypted);
}); });
it('Appstorage - encryptStorage & load encrypted storage works', async () => { it('Appstorage - encryptStorage & load encrypted storage works', async () => {
AsyncStorage.storageCache = {}; // cleanup from other tests
/** @type {AppStorage} */ /** @type {AppStorage} */
let Storage = new AppStorage(); let Storage = new AppStorage();
let w = new SegwitP2SHWallet(); let w = new SegwitP2SHWallet();
@ -236,7 +231,7 @@ it('Wallet can fetch balance', async () => {
assert.ok(w.getUnconfirmedBalance() === 0); assert.ok(w.getUnconfirmedBalance() === 0);
assert.ok(w._lastBalanceFetch === 0); assert.ok(w._lastBalanceFetch === 0);
await w.fetchBalance(); await w.fetchBalance();
assert.ok(w.getBalance() === 0.18262); assert.ok(w.getBalance() === 18262000);
assert.ok(w.getUnconfirmedBalance() === 0); assert.ok(w.getUnconfirmedBalance() === 0);
assert.ok(w._lastBalanceFetch > 0); assert.ok(w._lastBalanceFetch > 0);
}); });
@ -302,19 +297,18 @@ it('Wallet can fetch TXs', async () => {
describe('currency', () => { describe('currency', () => {
it('fetches exchange rate and saves to AsyncStorage', async () => { it('fetches exchange rate and saves to AsyncStorage', async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
AsyncStorage.storageCache = {}; // cleanup from other tests
let currency = require('./currency'); let currency = require('./currency');
await currency.startUpdater(); await currency.startUpdater();
let cur = AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]; let cur = await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES);
cur = JSON.parse(cur); cur = JSON.parse(cur);
assert.ok(Number.isInteger(cur[currency.STRUCT.LAST_UPDATED])); assert.ok(Number.isInteger(cur[currency.STRUCT.LAST_UPDATED]));
assert.ok(cur[currency.STRUCT.LAST_UPDATED] > 0); assert.ok(cur[currency.STRUCT.LAST_UPDATED] > 0);
assert.ok(cur['BTC_USD'] > 0); assert.ok(cur['BTC_USD'] > 0);
// now, setting other currency as default // now, setting other currency as default
AsyncStorage.storageCache[AppStorage.PREFERRED_CURRENCY] = JSON.stringify(FiatUnit.JPY); await AsyncStorage.setItem(AppStorage.PREFERRED_CURRENCY, JSON.stringify(FiatUnit.JPY));
await currency.startUpdater(); await currency.startUpdater();
cur = JSON.parse(AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]); cur = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES));
assert.ok(cur['BTC_JPY'] > 0); assert.ok(cur['BTC_JPY'] > 0);
// now setting with a proper setter // now setting with a proper setter
@ -322,7 +316,7 @@ describe('currency', () => {
await currency.startUpdater(); await currency.startUpdater();
let preferred = await currency.getPreferredCurrency(); let preferred = await currency.getPreferredCurrency();
assert.strictEqual(preferred.endPointKey, 'EUR'); assert.strictEqual(preferred.endPointKey, 'EUR');
cur = JSON.parse(AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]); cur = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES));
assert.ok(cur['BTC_EUR'] > 0); assert.ok(cur['BTC_EUR'] > 0);
}); });
}); });

2
BlueApp.js

@ -10,7 +10,7 @@ let A = require('./analytics');
let BlueElectrum = require('./BlueElectrum'); // eslint-disable-line let BlueElectrum = require('./BlueElectrum'); // eslint-disable-line
/** @type {AppStorage} */ /** @type {AppStorage} */
let BlueApp = new AppStorage(); const BlueApp = new AppStorage();
async function startAndDecrypt(retry) { async function startAndDecrypt(retry) {
console.log('startAndDecrypt'); console.log('startAndDecrypt');

61
BlueComponents.js

@ -25,7 +25,6 @@ import {
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import { LightningCustodianWallet } from './class'; import { LightningCustodianWallet } from './class';
import Carousel from 'react-native-snap-carousel'; import Carousel from 'react-native-snap-carousel';
import DeviceInfo from 'react-native-device-info';
import { BitcoinUnit } from './models/bitcoinUnits'; import { BitcoinUnit } from './models/bitcoinUnits';
import NavigationService from './NavigationService'; import NavigationService from './NavigationService';
import ImagePicker from 'react-native-image-picker'; import ImagePicker from 'react-native-image-picker';
@ -36,6 +35,7 @@ let loc = require('./loc/');
let BlueApp = require('./BlueApp'); let BlueApp = require('./BlueApp');
const { height, width } = Dimensions.get('window'); const { height, width } = Dimensions.get('window');
const aspectRatio = height / width; const aspectRatio = height / width;
const BigNumber = require('bignumber.js');
let isIpad; let isIpad;
if (aspectRatio > 1.6) { if (aspectRatio > 1.6) {
isIpad = false; isIpad = false;
@ -241,6 +241,14 @@ export class BlueCopyTextToClipboard extends Component {
this.state = { hasTappedText: false, address: props.text }; this.state = { hasTappedText: false, address: props.text };
} }
static getDerivedStateFromProps(props, state) {
if (state.hasTappedText) {
return { hasTappedText: state.hasTappedText, address: state.address };
} else {
return { hasTappedText: state.hasTappedText, address: props.text };
}
}
copyToClipboard = () => { copyToClipboard = () => {
this.setState({ hasTappedText: true }, () => { this.setState({ hasTappedText: true }, () => {
Clipboard.setString(this.props.text); Clipboard.setString(this.props.text);
@ -404,29 +412,6 @@ export class BlueFormMultiInput extends Component {
} }
} }
export class BlueFormInputAddress extends Component {
render() {
return (
<FormInput
{...this.props}
inputStyle={{
maxWidth: width - 110,
color: BlueApp.settings.foregroundColor,
fontSize: (isIpad && 10) || ((is.iphone8() && 12) || 14),
}}
containerStyle={{
marginTop: 5,
borderColor: BlueApp.settings.inputBorderColor,
borderBottomColor: BlueApp.settings.inputBorderColor,
borderWidth: 0.5,
borderBottomWidth: 0.5,
backgroundColor: BlueApp.settings.inputBackgroundColor,
}}
/>
);
}
}
export class BlueHeader extends Component { export class BlueHeader extends Component {
render() { render() {
return ( return (
@ -560,13 +545,6 @@ export class is {
static ipad() { static ipad() {
return isIpad; return isIpad;
} }
static iphone8() {
if (Platform.OS !== 'ios') {
return false;
}
return DeviceInfo.getDeviceId() === 'iPhone10,4';
}
} }
export class BlueSpacing20 extends Component { export class BlueSpacing20 extends Component {
@ -1733,7 +1711,7 @@ export class BlueAddressInput extends Component {
export class BlueBitcoinAmount extends Component { export class BlueBitcoinAmount extends Component {
static propTypes = { static propTypes = {
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), amount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
onChangeText: PropTypes.func, onChangeText: PropTypes.func,
disabled: PropTypes.bool, disabled: PropTypes.bool,
unit: PropTypes.string, unit: PropTypes.string,
@ -1744,8 +1722,15 @@ export class BlueBitcoinAmount extends Component {
}; };
render() { render() {
const amount = typeof this.props.amount === 'number' ? this.props.amount.toString() : this.props.amount; const amount = this.props.amount || 0;
let localCurrency = loc.formatBalanceWithoutSuffix(amount, BitcoinUnit.LOCAL_CURRENCY, false);
if (this.props.unit === BitcoinUnit.BTC) {
let sat = new BigNumber(amount);
sat = sat.multipliedBy(100000000).toString();
localCurrency = loc.formatBalanceWithoutSuffix(sat, BitcoinUnit.LOCAL_CURRENCY, false);
} else {
localCurrency = loc.formatBalanceWithoutSuffix(amount.toString(), BitcoinUnit.LOCAL_CURRENCY, false);
}
return ( return (
<TouchableWithoutFeedback disabled={this.props.pointerEvents === 'none'} onPress={() => this.textInput.focus()}> <TouchableWithoutFeedback disabled={this.props.pointerEvents === 'none'} onPress={() => this.textInput.focus()}>
<View> <View>
@ -1788,13 +1773,7 @@ export class BlueBitcoinAmount extends Component {
</Text> </Text>
</View> </View>
<View style={{ alignItems: 'center', marginBottom: 22, marginTop: 4 }}> <View style={{ alignItems: 'center', marginBottom: 22, marginTop: 4 }}>
<Text style={{ fontSize: 18, color: '#d4d4d4', fontWeight: '600' }}> <Text style={{ fontSize: 18, color: '#d4d4d4', fontWeight: '600' }}>{localCurrency}</Text>
{loc.formatBalance(
this.props.unit === BitcoinUnit.BTC ? amount || 0 : loc.formatBalanceWithoutSuffix(amount || 0, BitcoinUnit.BTC, false),
BitcoinUnit.LOCAL_CURRENCY,
false,
)}
</Text>
</View> </View>
</View> </View>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>

8
BlueElectrum.js

@ -1,10 +1,10 @@
import { AsyncStorage } from 'react-native'; import AsyncStorage from '@react-native-community/async-storage';
const ElectrumClient = require('electrum-client'); const ElectrumClient = require('electrum-client');
let bitcoin = require('bitcoinjs-lib'); let bitcoin = require('bitcoinjs-lib');
let reverse = require('buffer-reverse'); let reverse = require('buffer-reverse');
const storageKey = 'ELECTRUM_PEERS'; const storageKey = 'ELECTRUM_PEERS';
const defaultPeer = { host: 'electrum1.bluewallet.io', tcp: 50001 }; const defaultPeer = { host: 'electrum1.bluewallet.io', tcp: '50001' };
const hardcodedPeers = [ const hardcodedPeers = [
// { host: 'noveltybobble.coinjoined.com', tcp: '50001' }, // down // { host: 'noveltybobble.coinjoined.com', tcp: '50001' }, // down
// { host: 'electrum.be', tcp: '50001' }, // { host: 'electrum.be', tcp: '50001' },
@ -170,8 +170,8 @@ async function waitTillConnected() {
async function estimateFees() { async function estimateFees() {
if (!mainClient) throw new Error('Electrum client is not connected'); if (!mainClient) throw new Error('Electrum client is not connected');
const fast = await mainClient.blockchainEstimatefee(1); const fast = await mainClient.blockchainEstimatefee(1);
const medium = await mainClient.blockchainEstimatefee(6); const medium = await mainClient.blockchainEstimatefee(5);
const slow = await mainClient.blockchainEstimatefee(12); const slow = await mainClient.blockchainEstimatefee(10);
return { fast, medium, slow }; return { fast, medium, slow };
} }

5
Electrum.test.js

@ -14,8 +14,8 @@ beforeAll(async () => {
// while app starts up, but for tests we need to wait for it // while app starts up, but for tests we need to wait for it
try { try {
await BlueElectrum.waitTillConnected(); await BlueElectrum.waitTillConnected();
} catch (Err) { } catch (err) {
console.log('failed to connect to Electrum:', Err); console.log('failed to connect to Electrum:', err);
process.exit(1); process.exit(1);
} }
}); });
@ -52,7 +52,6 @@ describe('Electrum', () => {
hash = bitcoin.crypto.sha256(script); hash = bitcoin.crypto.sha256(script);
reversedHash = Buffer.from(hash.reverse()); reversedHash = Buffer.from(hash.reverse());
balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex')); balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
assert.ok(balance.confirmed === 51432);
// let peers = await mainClient.serverPeers_subscribe(); // let peers = await mainClient.serverPeers_subscribe();
// console.log(peers); // console.log(peers);

2
HDWallet.test.js

@ -207,7 +207,7 @@ it('Segwit HD (BIP49) can fetch balance with many used addresses in hierarchy',
let end = +new Date(); let end = +new Date();
const took = (end - start) / 1000; const took = (end - start) / 1000;
took > 15 && console.warn('took', took, "sec to fetch huge HD wallet's balance"); took > 15 && console.warn('took', took, "sec to fetch huge HD wallet's balance");
assert.strictEqual(hd.getBalance(), 0.00051432); assert.strictEqual(hd.getBalance(), 51432);
await hd.fetchUtxo(); await hd.fetchUtxo();
assert.ok(hd.utxo.length > 0); assert.ok(hd.utxo.length > 0);

138
WatchConnectivity.js

@ -0,0 +1,138 @@
import * as watch from 'react-native-watch-connectivity';
import { InteractionManager } from 'react-native';
const loc = require('./loc');
export default class WatchConnectivity {
isAppInstalled = false;
BlueApp = require('./BlueApp');
constructor() {
this.getIsWatchAppInstalled();
}
getIsWatchAppInstalled() {
watch.getIsWatchAppInstalled((err, isAppInstalled) => {
if (!err) {
this.isAppInstalled = isAppInstalled;
this.sendWalletsToWatch();
}
});
watch.subscribeToMessages(async (err, message, reply) => {
if (!err) {
if (message.request === 'createInvoice') {
const createInvoiceRequest = await this.handleLightningInvoiceCreateRequest(
message.walletIndex,
message.amount,
message.description,
);
reply({ invoicePaymentRequest: createInvoiceRequest });
}
} else {
reply(err);
}
});
}
async handleLightningInvoiceCreateRequest(walletIndex, amount, description) {
const wallet = this.BlueApp.getWallets()[walletIndex];
if (wallet.allowReceive() && amount > 0 && description.trim().length > 0) {
try {
const invoiceRequest = await wallet.addInvoice(amount, description);
return invoiceRequest;
} catch (error) {
return error;
}
}
}
async sendWalletsToWatch() {
InteractionManager.runAfterInteractions(async () => {
if (this.isAppInstalled) {
const allWallets = this.BlueApp.getWallets();
let wallets = [];
for (const wallet of allWallets) {
let receiveAddress = '';
if (wallet.allowReceive()) {
if (wallet.getAddressAsync) {
receiveAddress = await wallet.getAddressAsync();
} else {
receiveAddress = wallet.getAddress();
}
}
let transactions = wallet.getTransactions(10);
let watchTransactions = [];
for (const transaction of transactions) {
let type = 'pendingConfirmation';
let memo = '';
let amount = 0;
if (transaction.hasOwnProperty('confirmations') && !transaction.confirmations > 0) {
type = 'pendingConfirmation';
} else if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = transaction.timestamp + transaction.expire_time;
if (invoiceExpiration > now) {
type = 'pendingConfirmation';
} else if (invoiceExpiration < now) {
if (transaction.ispaid) {
type = 'received';
} else {
type = 'sent';
}
}
} else if (transaction.value / 100000000 < 0) {
type = 'sent';
} else {
type = 'received';
}
if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') {
if (isNaN(transaction.value)) {
amount = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = transaction.timestamp + transaction.expire_time;
if (invoiceExpiration > now) {
amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
} else if (invoiceExpiration < now) {
if (transaction.ispaid) {
amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
} else {
amount = loc.lnd.expired;
}
} else {
amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
}
} else {
amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
}
if (this.BlueApp.tx_metadata[transaction.hash] && this.BlueApp.tx_metadata[transaction.hash]['memo']) {
memo = this.BlueApp.tx_metadata[transaction.hash]['memo'];
} else if (transaction.memo) {
memo = transaction.memo;
}
const watchTX = { type, amount, memo, time: loc.transactionTimeToReadable(transaction.received) };
watchTransactions.push(watchTX);
}
wallets.push({
label: wallet.getLabel(),
balance: loc.formatBalance(Number(wallet.getBalance()), wallet.getPreferredBalanceUnit(), true),
type: wallet.type,
preferredBalanceUnit: wallet.getPreferredBalanceUnit(),
receiveAddress: receiveAddress,
transactions: watchTransactions,
});
}
watch.updateApplicationContext({ wallets });
}
});
}
}
WatchConnectivity.init = function() {
if (WatchConnectivity.shared) return;
WatchConnectivity.shared = new WatchConnectivity();
};

1
__mocks__/@react-native-community/async-storage.js

@ -0,0 +1 @@
export default from '@react-native-community/async-storage/jest/async-storage-mock'

151
android/app/app.iml

@ -17,7 +17,7 @@
<option name="ALLOW_USER_CONFIGURATION" value="false" /> <option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" /> <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" /> <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" /> <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/build/generated/res/rs/debug;file://$MODULE_DIR$/build/generated/res/resValues/debug" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" /> <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration> </configuration>
</facet> </facet>
@ -28,16 +28,16 @@
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debug/compileDebugRenderscript/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/react/debug" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/res/react/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debugAndroidTest/compileDebugAndroidTestAidl/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debugAndroidTest/compileDebugAndroidTestRenderscript/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
@ -48,13 +48,6 @@
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
@ -62,6 +55,13 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
@ -87,103 +87,106 @@
<excludeFolder url="file://$MODULE_DIR$/build/generated/source/r" /> <excludeFolder url="file://$MODULE_DIR$/build/generated/source/r" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundle_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/check_manifest_result" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkReleaseClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/external_libs_dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_app_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/legacy_multidex_aapt_derived_proguard_rules" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/legacy_multidex_aapt_derived_proguard_rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/legacy_multidex_main_dex_list" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/legacy_multidex_main_dex_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/linked_res_for_bundle" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/module_bundle" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/metadata_feature_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/signing_config" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/validate_signing_config" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" /> <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" /> <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content> </content>
<orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: org.webkit:android-jsc:r174650@aar" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp-urlconnection:3.12.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:1.10.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.react:react-native:0.57.8@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-fragment:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:localbroadcastmanager:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:1.10.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.11.0@jar" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.parse.bolts:bolts-tasks:1.4.0@jar" level="project" /> <orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.12.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:exifinterface:28.0.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:loader:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-v4:27.1.1@aar" level="project" /> <orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.0@aar" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp-urlconnection:3.11.0@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:cursoradapter:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:27.1.1@aar" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:1.10.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-compat:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:1.10.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: io.sentry:sentry:1.7.5@jar" level="project" /> <orderEntry type="library" name="Gradle: com.google.zxing:core:3.3.3@jar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0@jar" level="project" />
<orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" /> <orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui:27.1.1@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.6.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.infer.annotation:infer-annotation:0.11.2@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.24@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2@jar" level="project" /> <orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment:27.1.1@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: io.sentry:sentry-android:1.7.5@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-core-utils:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:27.1.1@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:interpolator:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:1.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.fasterxml.jackson.core:jackson-core:2.8.7@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:drawerlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.5.1@aar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:documentfile:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.parse.bolts:bolts-tasks:1.4.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:28.0.0@aar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support:multidex-instrumentation:1.0.2@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:collections:28.0.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:asynclayoutinflater:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:print:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.infer.annotation:infer-annotation:0.11.2@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:viewpager:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.react:react-native:0.59.6@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:customview:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:multidex:1.0.3@aar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.15.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.koushikdutta.async:androidasync:2.1.6@jar" level="project" />
<orderEntry type="module" module-name="react-native-device-info" />
<orderEntry type="module" module-name="react-native-google-analytics-bridge" />
<orderEntry type="module" module-name="react-native-obscure" />
<orderEntry type="module" module-name="react-native-camera" />
<orderEntry type="module" module-name="react-native-webview" /> <orderEntry type="module" module-name="react-native-webview" />
<orderEntry type="module" module-name="react-native-linear-gradient" /> <orderEntry type="module" module-name="@react-native-community_slider" />
<orderEntry type="module" module-name="react-native-svg" />
<orderEntry type="module" module-name="react-native-sentry" /> <orderEntry type="module" module-name="react-native-sentry" />
<orderEntry type="module" module-name="react-native-google-analytics-bridge" /> <orderEntry type="module" module-name="react-native-linear-gradient" />
<orderEntry type="module" module-name="react-native-image-picker" />
<orderEntry type="module" module-name="react-native-vector-icons" />
<orderEntry type="module" module-name="react-native-haptic-feedback" /> <orderEntry type="module" module-name="react-native-haptic-feedback" />
<orderEntry type="module" module-name="react-native-gesture-handler" /> <orderEntry type="module" module-name="@react-native-community_async-storage" />
<orderEntry type="module" module-name="react-native-fs" />
<orderEntry type="module" module-name="react-native-prompt-android" /> <orderEntry type="module" module-name="react-native-prompt-android" />
<orderEntry type="module" module-name="react-native-vector-icons" /> <orderEntry type="module" module-name="react-native-gesture-handler" />
<orderEntry type="module" module-name="react-native-device-info" />
<orderEntry type="module" module-name="react-native-randombytes" /> <orderEntry type="module" module-name="react-native-randombytes" />
<orderEntry type="module" module-name="react-native-camera" /> <orderEntry type="module" module-name="react-native-svg" />
<orderEntry type="module" module-name="@remobile_react-native-qrcode-local-image" />
<orderEntry type="module" module-name="react-native-fs" />
<orderEntry type="module" module-name="react-native-tcp" />
<orderEntry type="library" name="Gradle: android-android-27" level="project" />
</component> </component>
</module> </module>

3
android/app/build.gradle

@ -102,7 +102,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1 versionCode 1
versionName "3.9.7" versionName "3.9.8"
ndk { ndk {
abiFilters "armeabi-v7a", "x86" abiFilters "armeabi-v7a", "x86"
} }
@ -139,6 +139,7 @@ android {
} }
dependencies { dependencies {
implementation project(':@react-native-community_async-storage')
implementation project(':@react-native-community_slider') implementation project(':@react-native-community_slider')
implementation project(':react-native-obscure') implementation project(':react-native-obscure')
implementation project(':react-native-tcp') implementation project(':react-native-tcp')

BIN
android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf

Binary file not shown.

BIN
android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf

Binary file not shown.

BIN
android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf

Binary file not shown.

BIN
android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf

Binary file not shown.

BIN
android/app/src/main/assets/fonts/Octicons.ttf

Binary file not shown.

2
android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java

@ -3,6 +3,7 @@ package io.bluewallet.bluewallet;
import android.app.Application; import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
import com.reactnativecommunity.slider.ReactSliderPackage; import com.reactnativecommunity.slider.ReactSliderPackage;
import com.diegofhg.obscure.ObscurePackage; import com.diegofhg.obscure.ObscurePackage;
import com.peel.react.TcpSocketsModule; import com.peel.react.TcpSocketsModule;
@ -58,6 +59,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new AsyncStoragePackage(),
new ReactSliderPackage(), new ReactSliderPackage(),
new ObscurePackage(), new ObscurePackage(),
new TcpSocketsModule(), new TcpSocketsModule(),

2
android/settings.gradle

@ -1,4 +1,6 @@
rootProject.name = 'BlueWallet' rootProject.name = 'BlueWallet'
include ':@react-native-community_async-storage'
project(':@react-native-community_async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
include ':@react-native-community_slider' include ':@react-native-community_slider'
project(':@react-native-community_slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android') project(':@react-native-community_slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
include ':react-native-obscure' include ':react-native-obscure'

5
class/abstract-hd-wallet.js

@ -1,7 +1,6 @@
import { LegacyWallet } from './legacy-wallet'; import { LegacyWallet } from './legacy-wallet';
import Frisbee from 'frisbee'; import Frisbee from 'frisbee';
const bip39 = require('bip39'); const bip39 = require('bip39');
const BigNumber = require('bignumber.js');
const bitcoin = require('bitcoinjs-lib'); const bitcoin = require('bitcoinjs-lib');
const BlueElectrum = require('../BlueElectrum'); const BlueElectrum = require('../BlueElectrum');
@ -421,8 +420,8 @@ export class AbstractHDWallet extends LegacyWallet {
// finally fetching balance // finally fetching balance
let balance = await BlueElectrum.multiGetBalanceByAddress(this.usedAddresses); let balance = await BlueElectrum.multiGetBalanceByAddress(this.usedAddresses);
this.balance = new BigNumber(balance.balance).dividedBy(100000000).toNumber(); this.balance = balance.balance;
this.unconfirmed_balance = new BigNumber(balance.unconfirmed_balance).dividedBy(100000000).toNumber(); this.unconfirmed_balance = balance.unconfirmed_balance;
this._lastBalanceFetch = +new Date(); this._lastBalanceFetch = +new Date();
} catch (err) { } catch (err) {
console.warn(err); console.warn(err);

2
class/abstract-wallet.js

@ -95,7 +95,5 @@ export class AbstractWallet {
return 0; return 0;
} }
getAddress() {}
// createTx () { throw Error('not implemented') } // createTx () { throw Error('not implemented') }
} }

15
class/app-storage.js

@ -1,4 +1,4 @@
import { AsyncStorage } from 'react-native'; import AsyncStorage from '@react-native-community/async-storage';
import { import {
HDLegacyBreadwalletWallet, HDLegacyBreadwalletWallet,
HDSegwitP2SHWallet, HDSegwitP2SHWallet,
@ -9,7 +9,8 @@ import {
SegwitBech32Wallet, SegwitBech32Wallet,
} from './'; } from './';
import { LightningCustodianWallet } from './lightning-custodian-wallet'; import { LightningCustodianWallet } from './lightning-custodian-wallet';
let encryption = require('../encryption'); import WatchConnectivity from '../WatchConnectivity';
const encryption = require('../encryption');
export class AppStorage { export class AppStorage {
static FLAG_ENCRYPTED = 'data_encrypted'; static FLAG_ENCRYPTED = 'data_encrypted';
@ -118,8 +119,9 @@ export class AppStorage {
buckets = JSON.parse(buckets); buckets = JSON.parse(buckets);
buckets.push(encryption.encrypt(JSON.stringify(data), fakePassword)); buckets.push(encryption.encrypt(JSON.stringify(data), fakePassword));
this.cachedPassword = fakePassword; this.cachedPassword = fakePassword;
const bucketsString = JSON.stringify(buckets);
return AsyncStorage.setItem('data', JSON.stringify(buckets)); await AsyncStorage.setItem('data', bucketsString);
return (await AsyncStorage.getItem('data')) === bucketsString;
} }
/** /**
@ -199,6 +201,8 @@ export class AppStorage {
this.tx_metadata = data.tx_metadata; this.tx_metadata = data.tx_metadata;
} }
} }
WatchConnectivity.init();
await WatchConnectivity.shared.sendWalletsToWatch();
return true; return true;
} else { } else {
return false; // failed loading data or loading/decryptin data return false; // failed loading data or loading/decryptin data
@ -269,7 +273,8 @@ export class AppStorage {
} else { } else {
await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, ''); // drop the flag await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, ''); // drop the flag
} }
WatchConnectivity.init();
WatchConnectivity.shared.sendWalletsToWatch();
return AsyncStorage.setItem('data', JSON.stringify(data)); return AsyncStorage.setItem('data', JSON.stringify(data));
} }

3
class/legacy-wallet.js

@ -114,8 +114,7 @@ export class LegacyWallet extends AbstractWallet {
throw new Error('Could not fetch balance from API: ' + response.err + ' ' + JSON.stringify(response.body)); throw new Error('Could not fetch balance from API: ' + response.err + ' ' + JSON.stringify(response.body));
} }
this.balance = new BigNumber(json.final_balance); this.balance = Number(json.final_balance);
this.balance = this.balance.dividedBy(100000000).toString() * 1;
this.unconfirmed_balance = new BigNumber(json.unconfirmed_balance); this.unconfirmed_balance = new BigNumber(json.unconfirmed_balance);
this.unconfirmed_balance = this.unconfirmed_balance.dividedBy(100000000).toString() * 1; this.unconfirmed_balance = this.unconfirmed_balance.dividedBy(100000000).toString() * 1;
this._lastBalanceFetch = +new Date(); this._lastBalanceFetch = +new Date();

3
class/lightning-custodian-wallet.js

@ -1,7 +1,6 @@
import { LegacyWallet } from './legacy-wallet'; import { LegacyWallet } from './legacy-wallet';
import Frisbee from 'frisbee'; import Frisbee from 'frisbee';
import { BitcoinUnit, Chain } from '../models/bitcoinUnits'; import { BitcoinUnit, Chain } from '../models/bitcoinUnits';
let BigNumber = require('bignumber.js');
export class LightningCustodianWallet extends LegacyWallet { export class LightningCustodianWallet extends LegacyWallet {
static type = 'lightningCustodianWallet'; static type = 'lightningCustodianWallet';
@ -455,7 +454,7 @@ export class LightningCustodianWallet extends LegacyWallet {
} }
getBalance() { getBalance() {
return new BigNumber(this.balance).dividedBy(100000000).toString(10); return this.balance;
} }
async fetchBalance(noRetry) { async fetchBalance(noRetry) {

2
currency.js

@ -1,5 +1,5 @@
import Frisbee from 'frisbee'; import Frisbee from 'frisbee';
import { AsyncStorage } from 'react-native'; import AsyncStorage from '@react-native-community/async-storage';
import { AppStorage } from './class'; import { AppStorage } from './class';
import { FiatUnit } from './models/fiatUnit'; import { FiatUnit } from './models/fiatUnit';
let BigNumber = require('bignumber.js'); let BigNumber = require('bignumber.js');

2161
ios/BlueWallet.xcodeproj/project.pbxproj

File diff suppressed because it is too large

2
ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWallet-tvOS.xcscheme

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1010" LastUpgradeVersion = "1020"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "NO" parallelizeBuildables = "NO"

32
ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWallet.xcscheme

@ -1,25 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1010" LastUpgradeVersion = "1020"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "NO" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"
@ -34,20 +20,6 @@
ReferencedContainer = "container:BlueWallet.xcodeproj"> ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "BlueWalletTests.xctest"
BlueprintName = "BlueWalletTests"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
</BuildAction> </BuildAction>
<TestAction <TestAction

131
ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWalletWatch (Notification).xcscheme

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "BlueWallet.app"
BlueprintName = "BlueWallet"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "8"
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
launchAutomaticallySubstyle = "8"
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

128
ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWalletWatch.xcscheme

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "BlueWallet.app"
BlueprintName = "BlueWallet"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/BlueWallet">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
BuildableName = "BlueWalletWatch.app"
BlueprintName = "BlueWalletWatch"
ReferencedContainer = "container:BlueWallet.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

47
ios/BlueWallet.xcodeproj/xcuserdata/marcosrodriguez.xcuserdatad/xcschemes/xcschememanagement.plist

@ -4,15 +4,58 @@
<dict> <dict>
<key>SchemeUserState</key> <key>SchemeUserState</key>
<dict> <dict>
<key>BlueWallet for Apple Watch (Notification).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>78</integer>
</dict>
<key>BlueWallet for Apple Watch.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>71</integer>
</dict>
<key>BlueWallet-tvOS.xcscheme_^#shared#^_</key> <key>BlueWallet-tvOS.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>1</integer> <integer>0</integer>
</dict> </dict>
<key>BlueWallet.xcscheme_^#shared#^_</key> <key>BlueWallet.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>0</integer> <integer>1</integer>
</dict>
<key>BlueWalletWatch (Glance).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>14</integer>
</dict>
<key>BlueWalletWatch (Notification).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>BlueWalletWatch.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>00E356ED1AD99517003FC87E</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>13B07F861A680F5B00A75B9A</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>B40D4E2F225841EC00428FCC</key>
<dict>
<key>primary</key>
<true/>
</dict> </dict>
</dict> </dict>
</dict> </dict>

19
ios/BlueWallet.xcworkspace/contents.xcworkspacedata

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:BlueWallet.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
<FileRef
location = "group:../node_modules/react-native-tcp/ios/TcpSockets.xcodeproj">
</FileRef>
<FileRef
location = "group:../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage.xcodeproj">
</FileRef>
<FileRef
location = "group:../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot.xcodeproj">
</FileRef>
</Workspace>

8
ios/BlueWallet.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

6
ios/BlueWallet/AppDelegate.h

@ -6,9 +6,13 @@
*/ */
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@import WatchConnectivity;
@class WatchBridge;
@interface AppDelegate : UIResponder <UIApplicationDelegate> @interface AppDelegate : UIResponder <UIApplicationDelegate, WCSessionDelegate>
@property (nonatomic, strong) UIWindow *window; @property (nonatomic, strong) UIWindow *window;
@property(nonatomic, strong) WatchBridge *watchBridge;
@property(nonatomic, strong) WCSession *session;
@end @end

20
ios/BlueWallet/AppDelegate.m

@ -15,6 +15,7 @@
#else #else
#import "RNSentry.h" // This is used for versions of react < 0.40 #import "RNSentry.h" // This is used for versions of react < 0.40
#endif #endif
#import "WatchBridge.h"
@implementation AppDelegate @implementation AppDelegate
@ -35,6 +36,11 @@
rootViewController.view = rootView; rootViewController.view = rootView;
self.window.rootViewController = rootViewController; self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];
self.watchBridge = [WatchBridge shared];
self.session = self.watchBridge.session;
[self.session activateSession];
self.session.delegate = self;
return YES; return YES;
} }
@ -46,4 +52,18 @@
return NO; return NO;
} }
- (void)sessionDidDeactivate:(WCSession *)session {
[session activateSession];
}
- (void)session:(nonnull WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error {
}
- (void)sessionDidBecomeInactive:(nonnull WCSession *)session {
}
@end @end

2
ios/BlueWallet/Info.plist

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.9.7</string> <string>3.9.8</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>

56
ios/BlueWalletWatch Extension/ExtensionDelegate.swift

@ -0,0 +1,56 @@
//
// ExtensionDelegate.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/6/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
class ExtensionDelegate: NSObject, WKExtensionDelegate {
func applicationDidFinishLaunching() {
// Perform any final initialization of your application.
}
func applicationDidBecomeActive() {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillResignActive() {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, etc.
}
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
// Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one.
for task in backgroundTasks {
// Use a switch statement to check the task type
switch task {
case let backgroundTask as WKApplicationRefreshBackgroundTask:
// Be sure to complete the background task once youre done.
backgroundTask.setTaskCompletedWithSnapshot(false)
case let snapshotTask as WKSnapshotRefreshBackgroundTask:
// Snapshot tasks have a unique completion call, make sure to set your expiration date
snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil)
case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask:
// Be sure to complete the connectivity task once youre done.
connectivityTask.setTaskCompletedWithSnapshot(false)
case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
// Be sure to complete the URL session task once youre done.
urlSessionTask.setTaskCompletedWithSnapshot(false)
case let relevantShortcutTask as WKRelevantShortcutRefreshBackgroundTask:
// Be sure to complete the relevant-shortcut task once you're done.
relevantShortcutTask.setTaskCompletedWithSnapshot(false)
case let intentDidRunTask as WKIntentDidRunRefreshBackgroundTask:
// Be sure to complete the intent-did-run task once you're done.
intentDidRunTask.setTaskCompletedWithSnapshot(false)
default:
// make sure to complete unhandled task types
task.setTaskCompletedWithSnapshot(false)
}
}
}
}

38
ios/BlueWalletWatch Extension/Info.plist

@ -0,0 +1,38 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>BlueWalletWatch Extension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>3.9.8</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>io.bluewallet.bluewallet.watch</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
</dict>
</plist>

57
ios/BlueWalletWatch Extension/InterfaceController.swift

@ -0,0 +1,57 @@
//
// InterfaceController.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/6/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
import WatchConnectivity
import Foundation
class InterfaceController: WKInterfaceController {
@IBOutlet weak var walletsTable: WKInterfaceTable!
@IBOutlet weak var loadingIndicatorGroup: WKInterfaceGroup!
@IBOutlet weak var noWalletsAvailableLabel: WKInterfaceLabel!
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
WCSession.default.sendMessage(["message" : "sendApplicationContext"], replyHandler: nil, errorHandler: nil)
if (WatchDataSource.shared.wallets.isEmpty) {
loadingIndicatorGroup.setHidden(true)
noWalletsAvailableLabel.setHidden(false)
} else {
processWalletsTable()
}
NotificationCenter.default.addObserver(self, selector: #selector(processWalletsTable), name: WatchDataSource.NotificationName.dataUpdated, object: nil)
}
@objc private func processWalletsTable() {
loadingIndicatorGroup.setHidden(false)
walletsTable.setHidden(true)
walletsTable.setNumberOfRows(WatchDataSource.shared.wallets.count, withRowType: WalletInformation.identifier)
for index in 0..<walletsTable.numberOfRows {
guard let controller = walletsTable.rowController(at: index) as? WalletInformation else { continue }
let wallet = WatchDataSource.shared.wallets[index]
if wallet.identifier == nil {
WatchDataSource.shared.wallets[index].identifier = index
}
controller.name = wallet.label
controller.balance = wallet.balance
controller.type = WalletGradient(rawValue: wallet.type) ?? .SegwitHD
}
loadingIndicatorGroup.setHidden(true)
noWalletsAvailableLabel.setHidden(!WatchDataSource.shared.wallets.isEmpty)
walletsTable.setHidden(WatchDataSource.shared.wallets.isEmpty)
}
override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? {
return rowIndex;
}
}

38
ios/BlueWalletWatch Extension/NotificationController.swift

@ -0,0 +1,38 @@
//
// NotificationController.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/6/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
import Foundation
import UserNotifications
class NotificationController: WKUserNotificationInterfaceController {
override init() {
// Initialize variables here.
super.init()
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
override func didReceive(_ notification: UNNotification) {
// This method is called when a notification needs to be presented.
// Implement it if you use a dynamic notification interface.
// Populate your dynamic notification interface as quickly as possible.
}
}

155
ios/BlueWalletWatch Extension/NumericKeypadInterfaceController.swift

@ -0,0 +1,155 @@
//
// NumericKeypadInterfaceController.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/23/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
import Foundation
class NumericKeypadInterfaceController: WKInterfaceController {
static let identifier = "NumericKeypadInterfaceController"
private var amount: [String] = ["0"]
var keyPadType: NumericKeypadType = .BTC
struct NotificationName {
static let keypadDataChanged = Notification.Name(rawValue: "Notification.NumericKeypadInterfaceController.keypadDataChanged")
}
struct Notifications {
static let keypadDataChanged = Notification(name: NotificationName.keypadDataChanged)
}
enum NumericKeypadType: String {
case BTC = "BTC"
case SATS = "sats"
}
@IBOutlet weak var periodButton: WKInterfaceButton!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let context = context as? SpecifyInterfaceController.SpecificQRCodeContent {
amount = context.amountStringArray
keyPadType = context.bitcoinUnit
}
periodButton.setEnabled(keyPadType == .SATS)
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
updateTitle()
}
private func updateTitle() {
var title = ""
for amount in self.amount {
let isValid = Double(amount)
if amount == "." || isValid != nil {
title.append(String(amount))
}
}
if title.isEmpty {
title = "0"
}
setTitle("< \(title) \(keyPadType)")
NotificationCenter.default.post(name: NotificationName.keypadDataChanged, object: amount)
}
private func append(value: String) {
guard amount.filter({$0 != "."}).count <= 9 && !(amount.contains(".") && value == ".") else {
return
}
switch keyPadType {
case .SATS:
if amount.first == "0" {
if value == "0" {
return
}
amount[0] = value
} else {
amount.append(value)
}
case .BTC:
if amount.isEmpty {
if (value == "0") {
amount.append("0")
} else if value == "." && !amount.contains(".") {
amount.append("0")
amount.append(".")
} else {
amount.append(value)
}
} else if let first = amount.first, first == "0" {
if amount.count > 1, amount[1] != "." {
amount.insert(".", at: 1)
} else if amount.count == 1, amount.first == "0" && value != "." {
amount.append(".")
amount.append(value)
} else {
amount.append(value)
}
} else {
amount.append(value)
}
}
updateTitle()
}
@IBAction func keypadNumberOneTapped() {
append(value: "1")
}
@IBAction func keypadNumberTwoTapped() {
append(value: "2")
}
@IBAction func keypadNumberThreeTapped() {
append(value: "3")
}
@IBAction func keypadNumberFourTapped() {
append(value: "4")
}
@IBAction func keypadNumberFiveTapped() {
append(value: "5")
}
@IBAction func keypadNumberSixTapped() {
append(value: "6")
}
@IBAction func keypadNumberSevenTapped() {
append(value: "7")
}
@IBAction func keypadNumberEightTapped() {
append(value: "8")
}
@IBAction func keypadNumberNineTapped() {
append(value: "9")
}
@IBAction func keypadNumberZeroTapped() {
append(value: "0")
}
@IBAction func keypadNumberDotTapped() {
guard !amount.contains("."), keyPadType == .BTC else { return }
append(value: ".")
}
@IBAction func keypadNumberRemoveTapped() {
guard !amount.isEmpty else {
setTitle("< 0 \(keyPadType)")
return
}
amount.removeLast()
updateTitle()
}
}

39
ios/BlueWalletWatch Extension/Objects/Transaction.swift

@ -0,0 +1,39 @@
//
// Wallet.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/13/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import Foundation
class Transaction: NSObject, NSCoding {
static let identifier: String = "Transaction"
let time: String
let memo: String
let amount: String
let type: String
init(time: String, memo: String, type: String, amount: String) {
self.time = time
self.memo = memo
self.type = type
self.amount = amount
}
func encode(with aCoder: NSCoder) {
aCoder.encode(time, forKey: "time")
aCoder.encode(memo, forKey: "memo")
aCoder.encode(type, forKey: "type")
aCoder.encode(amount, forKey: "amount")
}
required init?(coder aDecoder: NSCoder) {
time = aDecoder.decodeObject(forKey: "time") as! String
memo = aDecoder.decodeObject(forKey: "memo") as! String
amount = aDecoder.decodeObject(forKey: "amount") as! String
type = aDecoder.decodeObject(forKey: "type") as! String
}
}

52
ios/BlueWalletWatch Extension/Objects/TransactionTableRow.swift

@ -0,0 +1,52 @@
//
// TransactionTableRow.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/10/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
class TransactionTableRow: NSObject {
@IBOutlet private weak var transactionAmountLabel: WKInterfaceLabel!
@IBOutlet private weak var transactionMemoLabel: WKInterfaceLabel!
@IBOutlet private weak var transactionTimeLabel: WKInterfaceLabel!
@IBOutlet private weak var transactionTypeImage: WKInterfaceImage!
static let identifier: String = "TransactionTableRow"
var amount: String = "" {
willSet {
transactionAmountLabel.setText(newValue)
}
}
var memo: String = "" {
willSet {
transactionMemoLabel.setText(newValue)
}
}
var time: String = "" {
willSet {
transactionTimeLabel.setText(newValue)
}
}
var type: String = "" {
willSet {
if (newValue == "pendingConfirmation") {
transactionTypeImage.setImage(UIImage(named: "pendingConfirmation"))
} else if (newValue == "received") {
transactionTypeImage.setImage(UIImage(named: "receivedArrow"))
} else if (newValue == "sent") {
transactionTypeImage.setImage(UIImage(named: "sentArrow"))
} else {
transactionTypeImage.setImage(nil)
}
}
}
}

51
ios/BlueWalletWatch Extension/Objects/Wallet.swift

@ -0,0 +1,51 @@
//
// Wallet.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/13/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import Foundation
class Wallet: NSObject, NSCoding {
static let identifier: String = "Wallet"
var identifier: Int?
let label: String
let balance: String
let type: String
let preferredBalanceUnit: String
let receiveAddress: String
let transactions: [Transaction]
init(label: String, balance: String, type: String, preferredBalanceUnit: String, receiveAddress: String, transactions: [Transaction], identifier: Int) {
self.label = label
self.balance = balance
self.type = type
self.preferredBalanceUnit = preferredBalanceUnit
self.receiveAddress = receiveAddress
self.transactions = transactions
self.identifier = identifier
}
func encode(with aCoder: NSCoder) {
aCoder.encode(label, forKey: "label")
aCoder.encode(balance, forKey: "balance")
aCoder.encode(type, forKey: "type")
aCoder.encode(receiveAddress, forKey: "receiveAddress")
aCoder.encode(preferredBalanceUnit, forKey: "preferredBalanceUnit")
aCoder.encode(transactions, forKey: "transactions")
aCoder.encode(identifier, forKey: "identifier")
}
required init?(coder aDecoder: NSCoder) {
label = aDecoder.decodeObject(forKey: "label") as! String
balance = aDecoder.decodeObject(forKey: "balance") as! String
type = aDecoder.decodeObject(forKey: "type") as! String
preferredBalanceUnit = aDecoder.decodeObject(forKey: "preferredBalanceUnit") as! String
receiveAddress = aDecoder.decodeObject(forKey: "receiveAddress") as! String
transactions = aDecoder.decodeObject(forKey: "transactions") as? [Transaction] ?? [Transaction]()
}
}

32
ios/BlueWalletWatch Extension/Objects/WalletGradient.swift

@ -0,0 +1,32 @@
//
// WalletGradient.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/23/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import Foundation
enum WalletGradient: String {
case SegwitHD = "HDsegwitP2SH"
case Segwit = "segwitP2SH"
case LightningCustodial = "lightningCustodianWallet"
case ACINQStrike = "LightningACINQ"
case WatchOnly = "watchOnly"
var imageString: String{
switch self {
case .Segwit:
return "wallet"
case .ACINQStrike:
return "walletACINQ"
case .SegwitHD:
return "walletHD"
case .WatchOnly:
return "walletWatchOnly"
case .LightningCustodial:
return "walletLightningCustodial"
}
}
}

36
ios/BlueWalletWatch Extension/Objects/WalletInformation.swift

@ -0,0 +1,36 @@
//
// WalletInformation.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/10/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
class WalletInformation: NSObject {
@IBOutlet private weak var walletBalanceLabel: WKInterfaceLabel!
@IBOutlet private weak var walletNameLabel: WKInterfaceLabel!
@IBOutlet private weak var walletGroup: WKInterfaceGroup!
static let identifier: String = "WalletInformation"
var name: String = "" {
willSet {
walletNameLabel.setText(newValue)
}
}
var balance: String = "" {
willSet {
walletBalanceLabel.setText(newValue)
}
}
var type: WalletGradient = .SegwitHD {
willSet {
walletGroup.setBackgroundImageNamed(newValue.imageString)
}
}
}

103
ios/BlueWalletWatch Extension/Objects/WatchDataSource.swift

@ -0,0 +1,103 @@
//
// WatchDataSource.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/20/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import Foundation
import WatchConnectivity
class WatchDataSource: NSObject, WCSessionDelegate {
struct NotificationName {
static let dataUpdated = Notification.Name(rawValue: "Notification.WalletDataSource.Updated")
}
struct Notifications {
static let dataUpdated = Notification(name: NotificationName.dataUpdated)
}
static let shared = WatchDataSource()
var wallets: [Wallet] = [Wallet]()
private let keychain = KeychainSwift()
override init() {
super.init()
if WCSession.isSupported() {
print("Activating watch session")
WCSession.default.delegate = self
WCSession.default.activate()
}
}
func processWalletsData(walletsInfo: [String: Any]) {
if let walletsToProcess = walletsInfo["wallets"] as? [[String: Any]] {
wallets.removeAll();
for (index, entry) in walletsToProcess.enumerated() {
guard let label = entry["label"] as? String, let balance = entry["balance"] as? String, let type = entry["type"] as? String, let preferredBalanceUnit = entry["preferredBalanceUnit"] as? String, let receiveAddress = entry["receiveAddress"] as? String, let transactions = entry["transactions"] as? [[String: Any]] else {
continue
}
var transactionsProcessed = [Transaction]()
for transactionEntry in transactions {
guard let time = transactionEntry["time"] as? String, let memo = transactionEntry["memo"] as? String, let amount = transactionEntry["amount"] as? String, let type = transactionEntry["type"] as? String else { continue }
let transaction = Transaction(time: time, memo: memo, type: type, amount: amount)
transactionsProcessed.append(transaction)
}
let wallet = Wallet(label: label, balance: balance, type: type, preferredBalanceUnit: preferredBalanceUnit, receiveAddress: receiveAddress, transactions: transactionsProcessed, identifier: index)
wallets.append(wallet)
}
if let walletsArchived = try? NSKeyedArchiver.archivedData(withRootObject: wallets, requiringSecureCoding: false) {
keychain.set(walletsArchived, forKey: Wallet.identifier)
}
WatchDataSource.postDataUpdatedNotification()
}
}
static func postDataUpdatedNotification() {
NotificationCenter.default.post(Notifications.dataUpdated)
}
static func requestLightningInvoice(walletIdentifier: Int, amount: Double, description: String?, responseHandler: @escaping (_ invoice: String) -> Void) {
guard WatchDataSource.shared.wallets.count > walletIdentifier else {
responseHandler("")
return
}
WCSession.default.sendMessage(["request": "createInvoice", "walletIndex": walletIdentifier, "amount": amount, "description": description ?? ""], replyHandler: { (reply: [String : Any]) in
if let invoicePaymentRequest = reply["invoicePaymentRequest"] as? String, !invoicePaymentRequest.isEmpty {
responseHandler(invoicePaymentRequest)
} else {
responseHandler("")
}
}) { (error) in
print(error)
responseHandler("")
}
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
WatchDataSource.shared.processWalletsData(walletsInfo: applicationContext)
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
WatchDataSource.shared.processWalletsData(walletsInfo: applicationContext)
}
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
// WatchDataSource.shared.processWalletsData(walletsInfo: userInfo)
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if activationState == .activated {
WCSession.default.sendMessage([:], replyHandler: nil, errorHandler: nil)
if let existingData = keychain.getData(Wallet.identifier), let walletData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(existingData) as? [Wallet] {
guard let walletData = walletData, walletData != self.wallets else { return }
wallets = walletData
WatchDataSource.postDataUpdatedNotification()
}
}
}
}

20
ios/BlueWalletWatch Extension/PushNotificationPayload.apns

@ -0,0 +1,20 @@
{
"aps": {
"alert": {
"body": "Test message",
"title": "Optional title",
"subtitle": "Optional subtitle"
},
"category": "myCategory",
"thread-id":"5280"
},
"WatchKit Simulator Actions": [
{
"title": "First Button",
"identifier": "firstButtonAction"
}
],
"customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App."
}

115
ios/BlueWalletWatch Extension/ReceiveInterfaceController.swift

@ -0,0 +1,115 @@
//
// ReceiveInterfaceController.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/12/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
import Foundation
import EFQRCode
class ReceiveInterfaceController: WKInterfaceController {
static let identifier = "ReceiveInterfaceController"
@IBOutlet weak var imageInterface: WKInterfaceImage!
private var wallet: Wallet?
private var isRenderingQRCode: Bool?
@IBOutlet weak var loadingIndicator: WKInterfaceGroup!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
guard let identifier = context as? Int, WatchDataSource.shared.wallets.count > identifier else {
pop()
return
}
let wallet = WatchDataSource.shared.wallets[identifier]
self.wallet = wallet
NotificationCenter.default.addObserver(forName: SpecifyInterfaceController.NotificationName.createQRCode, object: nil, queue: nil) { [weak self] (notification) in
self?.isRenderingQRCode = true
if let wallet = self?.wallet, wallet.type == "lightningCustodianWallet", let object = notification.object as? SpecifyInterfaceController.SpecificQRCodeContent, let amount = object.amount {
self?.imageInterface.setHidden(true)
self?.loadingIndicator.setHidden(false)
WatchDataSource.requestLightningInvoice(walletIdentifier: identifier, amount: amount, description: object.description, responseHandler: { (invoice) in
DispatchQueue.main.async {
if (!invoice.isEmpty) {
guard let cgImage = EFQRCode.generate(
content: "lightning:\(invoice)") else {
return
}
let image = UIImage(cgImage: cgImage)
self?.loadingIndicator.setHidden(true)
self?.imageInterface.setHidden(false)
self?.imageInterface.setImage(nil)
self?.imageInterface.setImage(image)
} else {
self?.pop()
self?.presentAlert(withTitle: "Error", message: "Unable to create invoice. Please, make sure your iPhone is paired and nearby.", preferredStyle: .alert, actions: [WKAlertAction(title: "OK", style: .default, handler: { [weak self] in
self?.dismiss()
})])
}
}
})
} else {
guard let notificationObject = notification.object as? SpecifyInterfaceController.SpecificQRCodeContent, let walletContext = self?.wallet, !walletContext.receiveAddress.isEmpty, let receiveAddress = self?.wallet?.receiveAddress else { return }
var address = "bitcoin:\(receiveAddress)"
var hasAmount = false
if let amount = notificationObject.amount {
address.append("?amount=\(amount)&")
hasAmount = true
}
if let description = notificationObject.description {
if (!hasAmount) {
address.append("?")
}
address.append("label=\(description)")
}
DispatchQueue.main.async {
guard let cgImage = EFQRCode.generate(
content: address) else {
return
}
let image = UIImage(cgImage: cgImage)
self?.imageInterface.setImage(nil)
self?.imageInterface.setImage(image)
self?.imageInterface.setHidden(false)
self?.loadingIndicator.setHidden(true)
self?.isRenderingQRCode = false
}
}
}
guard !wallet.receiveAddress.isEmpty, let cgImage = EFQRCode.generate(
content: wallet.receiveAddress) else {
return
}
let image = UIImage(cgImage: cgImage)
imageInterface.setImage(image)
}
override func didAppear() {
super.didAppear()
if wallet?.type == "lightningCustodianWallet" {
if isRenderingQRCode == nil {
presentController(withName: SpecifyInterfaceController.identifier, context: wallet?.identifier)
isRenderingQRCode = false
} else if isRenderingQRCode == false {
pop()
}
}
}
override func didDeactivate() {
super.didDeactivate()
NotificationCenter.default.removeObserver(self, name: SpecifyInterfaceController.NotificationName.createQRCode, object: nil)
}
@IBAction func specifyMenuItemTapped() {
presentController(withName: SpecifyInterfaceController.identifier, context: wallet?.identifier)
}
}

90
ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift

@ -0,0 +1,90 @@
//
// SpecifyInterfaceController.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/23/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
import Foundation
class SpecifyInterfaceController: WKInterfaceController {
static let identifier = "SpecifyInterfaceController"
@IBOutlet weak var descriptionButton: WKInterfaceButton!
@IBOutlet weak var amountButton: WKInterfaceButton!
struct SpecificQRCodeContent {
var amount: Double?
var description: String?
var amountStringArray: [String] = ["0"]
var bitcoinUnit: NumericKeypadInterfaceController.NumericKeypadType = .BTC
}
var specifiedQRContent: SpecificQRCodeContent = SpecificQRCodeContent(amount: nil, description: nil, amountStringArray: ["0"], bitcoinUnit: .BTC)
var wallet: Wallet?
struct NotificationName {
static let createQRCode = Notification.Name(rawValue: "Notification.SpecifyInterfaceController.createQRCode")
}
struct Notifications {
static let createQRCode = Notification(name: NotificationName.createQRCode)
}
override func awake(withContext context: Any?) {
super.awake(withContext: context)
guard let identifier = context as? Int, WatchDataSource.shared.wallets.count > identifier else {
return
}
let wallet = WatchDataSource.shared.wallets[identifier]
self.wallet = wallet
self.specifiedQRContent.bitcoinUnit = wallet.type == "lightningCustodianWallet" ? .SATS : .BTC
NotificationCenter.default.addObserver(forName: NumericKeypadInterfaceController.NotificationName.keypadDataChanged, object: nil, queue: nil) { [weak self] (notification) in
guard let amountObject = notification.object as? [String], !amountObject.isEmpty else { return }
if amountObject.count == 1 && (amountObject.first == "." || amountObject.first == "0") {
return
}
var title = ""
for amount in amountObject {
let isValid = Double(amount)
if amount == "." || isValid != nil {
title.append(String(amount))
}
}
self?.specifiedQRContent.amountStringArray = amountObject
if let amountDouble = Double(title), let keyPadType = self?.specifiedQRContent.bitcoinUnit {
self?.specifiedQRContent.amount = amountDouble
self?.amountButton.setTitle("\(title) \(keyPadType)")
}
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
NotificationCenter.default.removeObserver(self, name: NumericKeypadInterfaceController.NotificationName.keypadDataChanged, object: nil)
}
@IBAction func descriptionButtonTapped() {
presentTextInputController(withSuggestions: nil, allowedInputMode: .allowEmoji) { [weak self] (result: [Any]?) in
DispatchQueue.main.async {
if let result = result, let text = result.first as? String {
self?.specifiedQRContent.description = text
self?.descriptionButton.setTitle(nil)
self?.descriptionButton.setTitle(text)
}
}
}
}
@IBAction func createButtonTapped() {
NotificationCenter.default.post(name: NotificationName.createQRCode, object: specifiedQRContent)
dismiss()
}
override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
if segueIdentifier == NumericKeypadInterfaceController.identifier {
return specifiedQRContent
}
return nil
}
}

68
ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift

@ -0,0 +1,68 @@
//
// WalletDetailsInterfaceController.swift
// BlueWalletWatch Extension
//
// Created by Marcos Rodriguez on 3/11/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit
import Foundation
class WalletDetailsInterfaceController: WKInterfaceController {
var wallet: Wallet?
static let identifier = "WalletDetailsInterfaceController"
@IBOutlet weak var walletBasicsGroup: WKInterfaceGroup!
@IBOutlet weak var walletBalanceLabel: WKInterfaceLabel!
@IBOutlet weak var walletNameLabel: WKInterfaceLabel!
@IBOutlet weak var receiveButton: WKInterfaceButton!
@IBOutlet weak var noTransactionsLabel: WKInterfaceLabel!
@IBOutlet weak var transactionsTable: WKInterfaceTable!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
guard let identifier = context as? Int else {
pop()
return
}
let wallet = WatchDataSource.shared.wallets[identifier]
self.wallet = wallet
walletBalanceLabel.setText(wallet.balance)
walletNameLabel.setText(wallet.label)
walletBasicsGroup.setBackgroundImageNamed(WalletGradient(rawValue: wallet.type)?.imageString)
processWalletsTable()
}
override func willActivate() {
super.willActivate()
transactionsTable.setHidden(wallet?.transactions.isEmpty ?? true)
noTransactionsLabel.setHidden(!(wallet?.transactions.isEmpty ?? false))
}
@IBAction func receiveMenuItemTapped() {
presentController(withName: ReceiveInterfaceController.identifier, context: wallet)
}
@objc private func processWalletsTable() {
transactionsTable.setNumberOfRows(wallet?.transactions.count ?? 0, withRowType: TransactionTableRow.identifier)
for index in 0..<transactionsTable.numberOfRows {
guard let controller = transactionsTable.rowController(at: index) as? TransactionTableRow, let transaction = wallet?.transactions[index] else { continue }
controller.amount = transaction.amount
controller.type = transaction.type
controller.memo = transaction.memo
controller.time = transaction.time
}
transactionsTable.setHidden(wallet?.transactions.isEmpty ?? true)
noTransactionsLabel.setHidden(!(wallet?.transactions.isEmpty ?? false))
}
override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
return wallet?.identifier
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/1024.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/58.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/87.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

92
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Contents.json

@ -0,0 +1,92 @@
{
"images" : [
{
"size" : "24x24",
"idiom" : "watch",
"filename" : "Icon-48.png",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"filename" : "Icon-55.png",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "58.png",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "87.png",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"filename" : "watch.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"filename" : "Icon-88.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "40mm"
},
{
"size" : "50x50",
"idiom" : "watch",
"filename" : "Icon-173.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "44mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"filename" : "Icon-172.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"filename" : "Icon-196.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
},
{
"size" : "108x108",
"idiom" : "watch",
"filename" : "group-copy-2@3x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "44mm"
},
{
"size" : "1024x1024",
"idiom" : "watch-marketing",
"filename" : "1024.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-172.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-173.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-196.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-55.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-88.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/group-copy-2@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/watch.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

6
ios/BlueWalletWatch/Assets.xcassets/Contents.json

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

13
ios/BlueWalletWatch/Assets.xcassets/loadingIndicator.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "group-copy-2@3x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/loadingIndicator.imageset/group-copy-2@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

13
ios/BlueWalletWatch/Assets.xcassets/pendingConfirmation.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "shape@3x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/pendingConfirmation.imageset/shape@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

13
ios/BlueWalletWatch/Assets.xcassets/qr-code.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "qr-code@3x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/qr-code.imageset/qr-code@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

13
ios/BlueWalletWatch/Assets.xcassets/receivedArrow.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "path-copy-3@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/receivedArrow.imageset/path-copy-3@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

13
ios/BlueWalletWatch/Assets.xcassets/sentArrow.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "path-copy@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/sentArrow.imageset/path-copy@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

23
ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/Contents.json

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "mask.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mask@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "mask@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

23
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/Contents.json

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "mask.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mask@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "mask@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

13
ios/BlueWalletWatch/Assets.xcassets/walletHD.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "mask@3x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/walletHD.imageset/mask@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

13
ios/BlueWalletWatch/Assets.xcassets/walletLightningCustodial.imageset/Contents.json

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "watch",
"filename" : "mask@3x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/walletLightningCustodial.imageset/mask@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

23
ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/Contents.json

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "mask.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mask@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "mask@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

339
ios/BlueWalletWatch/Base.lproj/Interface.storyboard

@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="14490.70" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="AgC-eL-Hgc">
<device id="watch44" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="watchOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="14490.21"/>
</dependencies>
<scenes>
<!--BlueWallet-->
<scene sceneID="aou-V4-d1y">
<objects>
<controller title="BlueWallet" fullBounds="YES" id="AgC-eL-Hgc" customClass="InterfaceController" customModule="BlueWalletWatch" customModuleProvider="target">
<items>
<table alignment="left" id="jUH-JS-ccp">
<items>
<tableRow identifier="WalletInformation" id="Rdv-UZ-gaS" customClass="WalletInformation" customModule="BlueWalletWatch_Extension">
<group key="rootItem" width="1" height="66.5" alignment="left" backgroundImage="walletHD" radius="8" id="H28-wi-Sks" customClass="WalletInformation" customModule="BlueWalletWatch_Extension">
<items>
<label width="6" alignment="left" id="RJV-QC-scb"/>
<group width="1" alignment="center" verticalAlignment="center" layout="vertical" id="UrU-xX-jYW">
<items>
<label width="1" alignment="left" text="Balance" minimumScaleFactor="0.5" id="QYx-3e-6zf">
<fontDescription key="font" style="UICTFontTextStyleHeadline"/>
</label>
<label alignment="left" text="Wallet" id="qpj-I1-cWt"/>
</items>
</group>
</items>
</group>
<connections>
<outlet property="walletBalanceLabel" destination="QYx-3e-6zf" id="cfa-2U-FBQ"/>
<outlet property="walletGroup" destination="H28-wi-Sks" id="ydq-d4-4eb"/>
<outlet property="walletNameLabel" destination="qpj-I1-cWt" id="dd9-XB-XMc"/>
<segue destination="XWa-4i-Abg" kind="push" identifier="WalletDetailsInterfaceController" id="Qts-pn-15q"/>
</connections>
</tableRow>
</items>
</table>
<group width="1" alignment="left" verticalAlignment="center" layout="vertical" id="1Db-mZ-yxl">
<items>
<imageView width="60" height="60" alignment="center" image="loadingIndicator" contentMode="scaleAspectFit" id="dLA-pP-GUU"/>
<label alignment="center" text="Loading wallets..." id="hCG-Eg-bck"/>
</items>
</group>
<label alignment="center" verticalAlignment="center" hidden="YES" text="No wallets available. Please, add one by opening BlueWallet on your iPhone." textAlignment="center" numberOfLines="0" id="I2I-8t-hp3"/>
</items>
<connections>
<outlet property="loadingIndicatorGroup" destination="1Db-mZ-yxl" id="XPX-Y8-dv0"/>
<outlet property="noWalletsAvailableLabel" destination="I2I-8t-hp3" id="c4O-Mg-3ps"/>
<outlet property="walletsTable" destination="jUH-JS-ccp" id="ONe-Gg-EJn"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="220" y="345"/>
</scene>
<!--WalletDetailsInterfaceController-->
<scene sceneID="KqX-Cy-IJm">
<objects>
<controller identifier="WalletDetailsInterfaceController" id="XWa-4i-Abg" customClass="WalletDetailsInterfaceController" customModule="BlueWalletWatch_Extension">
<items>
<group width="1" height="66.5" alignment="left" backgroundImage="walletHD" radius="8" id="275-K7-Qhe" customClass="WalletInformation" customModule="BlueWalletWatch_Extension">
<items>
<label width="6" alignment="left" id="QMf-Fm-1cw"/>
<group width="1" widthAdjustment="-10" alignment="center" verticalAlignment="center" layout="vertical" id="jx2-si-OEm">
<items>
<label alignment="left" text="Balance" minimumScaleFactor="0.5" id="WTr-jJ-w7L">
<fontDescription key="font" style="UICTFontTextStyleHeadline"/>
</label>
<label width="1" alignment="left" text="Wallet" id="PQi-JV-aYW"/>
</items>
</group>
</items>
</group>
<button width="1" alignment="left" title="Receive" id="bPO-h8-ccD">
<color key="titleColor" red="0.18431372549019609" green="0.37254901960784315" blue="0.70196078431372544" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="0.80000000000000004" green="0.8666666666666667" blue="0.97647058823529409" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="16"/>
<connections>
<segue destination="egq-Yw-qK5" kind="push" identifier="ReceiveInterfaceController" id="zEG-Xi-Smb"/>
</connections>
</button>
<label alignment="center" verticalAlignment="bottom" text="No Transactions" textAlignment="left" id="pi4-Bk-Jiq"/>
<table alignment="left" id="nyQ-lX-DX0">
<items>
<tableRow identifier="TransactionTableRow" id="HuQ-ep-L9j" customClass="TransactionTableRow" customModule="BlueWalletWatch_Extension">
<group key="rootItem" width="1" height="0.0" alignment="left" id="3X8-cc-rOv">
<items>
<button alignment="left" id="NEN-rG-rmr">
<group key="contentGroup" width="1" alignment="left" id="NY7-0s-nLc">
<items>
<imageView width="23" height="16" alignment="left" verticalAlignment="center" image="pendingConfirmation" contentMode="scaleAspectFit" id="hWs-WA-db1"/>
<group width="1" alignment="left" layout="vertical" spacing="8" id="Tes-g9-rp0">
<items>
<label width="1" alignment="left" text="Time" minimumScaleFactor="0.10000000000000001" id="GqE-KB-TRD">
<fontDescription key="font" style="UICTFontTextStyleSubhead"/>
</label>
<label width="1" alignment="left" verticalAlignment="bottom" text="memo" numberOfLines="0" id="AJ8-p9-ID7">
<color key="textColor" red="0.63137254901960782" green="0.63137254901960782" blue="0.63137254901960782" alpha="0.84999999999999998" colorSpace="calibratedRGB"/>
<fontDescription key="font" style="UICTFontTextStyleSubhead"/>
</label>
<label width="1" alignment="left" text="Amount" textAlignment="left" minimumScaleFactor="0.10000000000000001" id="sAS-LI-RY7">
<fontDescription key="font" style="UICTFontTextStyleSubhead"/>
</label>
</items>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="margins" left="4" right="27" top="8" bottom="8"/>
</group>
</items>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</group>
</button>
</items>
</group>
<connections>
<outlet property="transactionAmountLabel" destination="sAS-LI-RY7" id="TkE-JU-sRF"/>
<outlet property="transactionMemoLabel" destination="AJ8-p9-ID7" id="9I4-VO-d0H"/>
<outlet property="transactionTimeLabel" destination="GqE-KB-TRD" id="idk-sO-obD"/>
<outlet property="transactionTypeImage" destination="hWs-WA-db1" id="BF8-T8-T5U"/>
</connections>
</tableRow>
</items>
</table>
</items>
<connections>
<outlet property="noTransactionsLabel" destination="pi4-Bk-Jiq" id="zft-Hw-KuZ"/>
<outlet property="receiveButton" destination="bPO-h8-ccD" id="xBq-42-9qP"/>
<outlet property="transactionsTable" destination="nyQ-lX-DX0" id="N1x-px-s08"/>
<outlet property="walletBalanceLabel" destination="WTr-jJ-w7L" id="kiU-ZS-2dh"/>
<outlet property="walletBasicsGroup" destination="275-K7-Qhe" id="nvB-rn-8Xn"/>
<outlet property="walletNameLabel" destination="PQi-JV-aYW" id="dfi-Ai-rOe"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="467.86956521739125" y="344.55357142857144"/>
</scene>
<!--Static Notification Interface Controller-->
<scene sceneID="AEw-b0-oYE">
<objects>
<notificationController id="YCC-NB-fut">
<items>
<label alignment="left" text="Alert Label" numberOfLines="0" id="IdU-wH-bcW"/>
</items>
<notificationCategory key="notificationCategory" identifier="myCategory" id="JfB-70-Muf"/>
<connections>
<outlet property="notificationAlertLabel" destination="IdU-wH-bcW" id="JKC-fr-R95"/>
<segue destination="4sK-HA-Art" kind="relationship" relationship="dynamicNotificationInterface" id="kXh-Jw-8B1"/>
<segue destination="eXb-UN-Cd0" kind="relationship" relationship="dynamicInteractiveNotificationInterface" id="mpB-YA-K8N"/>
</connections>
</notificationController>
</objects>
<point key="canvasLocation" x="220" y="643"/>
</scene>
<!--Notification Controller-->
<scene sceneID="ZPc-GJ-vnh">
<objects>
<controller id="4sK-HA-Art" customClass="NotificationController" customModule="BlueWalletWatch" customModuleProvider="target"/>
</objects>
<point key="canvasLocation" x="468" y="643"/>
</scene>
<!--ReceiveInterfaceController-->
<scene sceneID="tQ7-Qr-5i4">
<objects>
<controller identifier="ReceiveInterfaceController" fullBounds="YES" fullScreen="YES" id="egq-Yw-qK5" customClass="ReceiveInterfaceController" customModule="BlueWalletWatch_Extension">
<items>
<imageView height="1" alignment="left" id="Dnb-sM-wdN"/>
<group width="1" alignment="center" verticalAlignment="center" hidden="YES" layout="vertical" id="0If-FP-smM">
<items>
<imageView width="60" height="60" alignment="center" image="loadingIndicator" contentMode="scaleAspectFit" id="nQb-s6-ySB"/>
<label alignment="center" text="Creating Invoice..." id="n5f-iL-ib7"/>
</items>
</group>
</items>
<menu key="menu" id="GDw-hN-TVp">
<items>
<menuItem title="Customize" icon="more" id="RHB-IJ-Utd">
<connections>
<action selector="specifyMenuItemTapped" destination="egq-Yw-qK5" id="KMQ-wI-vdE"/>
</connections>
</menuItem>
</items>
</menu>
<edgeInsets key="margins" left="4" right="4" top="4" bottom="4"/>
<connections>
<outlet property="imageInterface" destination="Dnb-sM-wdN" id="z1e-zC-anB"/>
<outlet property="loadingIndicator" destination="0If-FP-smM" id="Wtf-mm-8Ke"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="716" y="345"/>
</scene>
<!--SpecifyInterfaceController-->
<scene sceneID="erR-Ld-VGW">
<objects>
<controller identifier="SpecifyInterfaceController" id="aUg-UP-Vh5" customClass="SpecifyInterfaceController" customModule="BlueWalletWatch_Extension">
<items>
<button width="1" alignment="left" title="Description" id="fcI-6Z-moQ">
<connections>
<action selector="descriptionButtonTapped" destination="aUg-UP-Vh5" id="ZT5-rL-QZq"/>
</connections>
</button>
<button width="1" alignment="left" title="Amount" id="0Hm-hv-Yi3">
<connections>
<segue destination="2PN-Fb-8j5" kind="modal" identifier="NumericKeypadInterfaceController" id="LlG-6l-ghO"/>
</connections>
</button>
<separator alignment="left" alpha="0.0" id="i7u-PI-g7Q">
<color key="color" red="0.63137254899999995" green="0.63137254899999995" blue="0.63137254899999995" alpha="0.84999999999999998" colorSpace="calibratedRGB"/>
</separator>
<button width="1" alignment="left" verticalAlignment="bottom" title="Create" id="6eh-lx-UEe">
<color key="titleColor" red="0.1843137255" green="0.37254901959999998" blue="0.70196078429999997" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="0.80000000000000004" green="0.86666666670000003" blue="0.97647058819999999" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="16"/>
<connections>
<action selector="createButtonTapped" destination="aUg-UP-Vh5" id="dnh-3i-jIE"/>
</connections>
</button>
</items>
<connections>
<outlet property="amountButton" destination="0Hm-hv-Yi3" id="9DN-zh-BGB"/>
<outlet property="descriptionButton" destination="fcI-6Z-moQ" id="a7M-ZD-Zsi"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="967" y="357"/>
</scene>
<!--NumericKeypadInterfaceController-->
<scene sceneID="4Mp-O7-Llm">
<objects>
<controller identifier="NumericKeypadInterfaceController" fullBounds="YES" id="2PN-Fb-8j5" customClass="NumericKeypadInterfaceController" customModule="BlueWalletWatch_Extension">
<items>
<group height="0.25" alignment="left" id="kaq-2v-f7r">
<items>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="1" id="ghD-Jq-ubw">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberOneTapped" destination="2PN-Fb-8j5" id="n6o-GR-D7i"/>
</connections>
</button>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="2" id="aUI-EE-NVw">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberTwoTapped" destination="2PN-Fb-8j5" id="pfD-Db-6od"/>
</connections>
</button>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="3" id="TKO-lc-aYf">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberThreeTapped" destination="2PN-Fb-8j5" id="fqm-0L-U6Z"/>
</connections>
</button>
</items>
</group>
<group height="0.25" alignment="left" verticalAlignment="center" id="JB4-ZC-T8y">
<items>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="4" id="kH2-N1-Hbe">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberFourTapped" destination="2PN-Fb-8j5" id="r24-dK-OUA"/>
</connections>
</button>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="5" id="AA6-Gq-qRe">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberFiveTapped" destination="2PN-Fb-8j5" id="yTW-cf-ZCP"/>
</connections>
</button>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="6" id="Nt9-we-M9f">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberSixTapped" destination="2PN-Fb-8j5" id="xOh-ab-nWm"/>
</connections>
</button>
</items>
</group>
<group height="0.25" alignment="left" verticalAlignment="center" id="CT1-xK-izT">
<items>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="7" id="ohU-B0-mvg">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberSevenTapped" destination="2PN-Fb-8j5" id="8CA-Q5-XZt"/>
</connections>
</button>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="8" id="3FQ-tZ-9kd">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberEightTapped" destination="2PN-Fb-8j5" id="4h8-vi-GjT"/>
</connections>
</button>
<button width="0.33300000000000002" alignment="left" verticalAlignment="center" title="9" id="NJM-uR-nyO">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberNineTapped" destination="2PN-Fb-8j5" id="qpZ-nf-E5y"/>
</connections>
</button>
</items>
</group>
<group height="0.25" alignment="left" verticalAlignment="center" id="hqA-Nb-d5C">
<items>
<button width="0.33300000000000002" height="1" alignment="left" verticalAlignment="center" title="." id="g6Z-9t-ahQ">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberDotTapped" destination="2PN-Fb-8j5" id="K7P-bQ-h24"/>
</connections>
</button>
<button width="0.33300000000000002" height="1" alignment="left" verticalAlignment="center" title="0" id="S1H-Id-l6g">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberZeroTapped" destination="2PN-Fb-8j5" id="YH1-KR-oIu"/>
</connections>
</button>
<button width="0.33300000000000002" height="1" alignment="left" verticalAlignment="center" title="&lt;" id="q8Q-tK-nzd">
<fontDescription key="font" type="system" weight="heavy" pointSize="15"/>
<connections>
<action selector="keypadNumberRemoveTapped" destination="2PN-Fb-8j5" id="l7u-ZB-AyF"/>
</connections>
</button>
</items>
</group>
</items>
<connections>
<outlet property="periodButton" destination="g6Z-9t-ahQ" id="ynz-0C-Fxe"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="1197" y="357"/>
</scene>
<!--Notification Controller-->
<scene sceneID="Niz-AI-uX2">
<objects>
<controller id="eXb-UN-Cd0" customClass="NotificationController" customModule="BlueWalletWatch" customModuleProvider="target"/>
</objects>
<point key="canvasLocation" x="220" y="1029"/>
</scene>
</scenes>
<color key="tintColor" red="0.40784313725490196" green="0.73333333333333328" blue="0.88235294117647056" alpha="1" colorSpace="calibratedRGB"/>
</document>

33
ios/BlueWalletWatch/Info.plist

@ -0,0 +1,33 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>BlueWallet</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.9.8</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKCompanionAppBundleIdentifier</key>
<string>io.bluewallet.bluewallet</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>

454
ios/KeychainSwiftDistrib.swift

@ -0,0 +1,454 @@
//
// Keychain helper for iOS/Swift.
//
// https://github.com/evgenyneu/keychain-swift
//
// This file was automatically generated by combining multiple Swift source files.
//
// ----------------------------
//
// KeychainSwift.swift
//
// ----------------------------
import Security
import Foundation
/**
A collection of helper functions for saving text and data in the keychain.
*/
open class KeychainSwift {
var lastQueryParameters: [String: Any]? // Used by the unit tests
/// Contains result code from the last operation. Value is noErr (0) for a successful result.
open var lastResultCode: OSStatus = noErr
var keyPrefix = "" // Can be useful in test.
/**
Specify an access group that will be used to access keychain items. Access groups can be used to share keychain items between applications. When access group value is nil all application access groups are being accessed. Access group name is used by all functions: set, get, delete and clear.
*/
open var accessGroup: String?
/**
Specifies whether the items can be synchronized with other devices through iCloud. Setting this property to true will
add the item to other devices with the `set` method and obtain synchronizable items with the `get` command. Deleting synchronizable items will remove them from all devices. In order for keychain synchronization to work the user must enable "Keychain" in iCloud settings.
Does not work on macOS.
*/
open var synchronizable: Bool = false
private let readLock = NSLock()
/// Instantiate a KeychainSwift object
public init() { }
/**
- parameter keyPrefix: a prefix that is added before the key in get/set methods. Note that `clear` method still clears everything from the Keychain.
*/
public init(keyPrefix: String) {
self.keyPrefix = keyPrefix
}
/**
Stores the text value in the keychain item under the given key.
- parameter key: Key under which the text value is stored in the keychain.
- parameter value: Text string to be written to the keychain.
- parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user.
- returns: True if the text was successfully written to the keychain.
*/
@discardableResult
open func set(_ value: String, forKey key: String,
withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool {
if let value = value.data(using: String.Encoding.utf8) {
return set(value, forKey: key, withAccess: access)
}
return false
}
/**
Stores the data in the keychain item under the given key.
- parameter key: Key under which the data is stored in the keychain.
- parameter value: Data to be written to the keychain.
- parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user.
- returns: True if the text was successfully written to the keychain.
*/
@discardableResult
open func set(_ value: Data, forKey key: String,
withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool {
delete(key) // Delete any existing key before saving it
let accessible = access?.value ?? KeychainSwiftAccessOptions.defaultOption.value
let prefixedKey = keyWithPrefix(key)
var query: [String : Any] = [
KeychainSwiftConstants.klass : kSecClassGenericPassword,
KeychainSwiftConstants.attrAccount : prefixedKey,
KeychainSwiftConstants.valueData : value,
KeychainSwiftConstants.accessible : accessible
]
query = addAccessGroupWhenPresent(query)
query = addSynchronizableIfRequired(query, addingItems: true)
lastQueryParameters = query
lastResultCode = SecItemAdd(query as CFDictionary, nil)
return lastResultCode == noErr
}
/**
Stores the boolean value in the keychain item under the given key.
- parameter key: Key under which the value is stored in the keychain.
- parameter value: Boolean to be written to the keychain.
- parameter withAccess: Value that indicates when your app needs access to the value in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user.
- returns: True if the value was successfully written to the keychain.
*/
@discardableResult
open func set(_ value: Bool, forKey key: String,
withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool {
let bytes: [UInt8] = value ? [1] : [0]
let data = Data(bytes)
return set(data, forKey: key, withAccess: access)
}
/**
Retrieves the text value from the keychain that corresponds to the given key.
- parameter key: The key that is used to read the keychain item.
- returns: The text value from the keychain. Returns nil if unable to read the item.
*/
open func get(_ key: String) -> String? {
if let data = getData(key) {
if let currentString = String(data: data, encoding: .utf8) {
return currentString
}
lastResultCode = -67853 // errSecInvalidEncoding
}
return nil
}
/**
Retrieves the data from the keychain that corresponds to the given key.
- parameter key: The key that is used to read the keychain item.
- returns: The text value from the keychain. Returns nil if unable to read the item.
*/
open func getData(_ key: String) -> Data? {
// The lock prevents the code to be run simlultaneously
// from multiple threads which may result in crashing
readLock.lock()
defer { readLock.unlock() }
let prefixedKey = keyWithPrefix(key)
var query: [String: Any] = [
KeychainSwiftConstants.klass : kSecClassGenericPassword,
KeychainSwiftConstants.attrAccount : prefixedKey,
KeychainSwiftConstants.returnData : kCFBooleanTrue!,
KeychainSwiftConstants.matchLimit : kSecMatchLimitOne
]
query = addAccessGroupWhenPresent(query)
query = addSynchronizableIfRequired(query, addingItems: false)
lastQueryParameters = query
var result: AnyObject?
lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
if lastResultCode == noErr { return result as? Data }
return nil
}
/**
Retrieves the boolean value from the keychain that corresponds to the given key.
- parameter key: The key that is used to read the keychain item.
- returns: The boolean value from the keychain. Returns nil if unable to read the item.
*/
open func getBool(_ key: String) -> Bool? {
guard let data = getData(key) else { return nil }
guard let firstBit = data.first else { return nil }
return firstBit == 1
}
/**
Deletes the single keychain item specified by the key.
- parameter key: The key that is used to delete the keychain item.
- returns: True if the item was successfully deleted.
*/
@discardableResult
open func delete(_ key: String) -> Bool {
let prefixedKey = keyWithPrefix(key)
var query: [String: Any] = [
KeychainSwiftConstants.klass : kSecClassGenericPassword,
KeychainSwiftConstants.attrAccount : prefixedKey
]
query = addAccessGroupWhenPresent(query)
query = addSynchronizableIfRequired(query, addingItems: false)
lastQueryParameters = query
lastResultCode = SecItemDelete(query as CFDictionary)
return lastResultCode == noErr
}
/**
Deletes all Keychain items used by the app. Note that this method deletes all items regardless of the prefix settings used for initializing the class.
- returns: True if the keychain items were successfully deleted.
*/
@discardableResult
open func clear() -> Bool {
var query: [String: Any] = [ kSecClass as String : kSecClassGenericPassword ]
query = addAccessGroupWhenPresent(query)
query = addSynchronizableIfRequired(query, addingItems: false)
lastQueryParameters = query
lastResultCode = SecItemDelete(query as CFDictionary)
return lastResultCode == noErr
}
/// Returns the key with currently set prefix.
func keyWithPrefix(_ key: String) -> String {
return "\(keyPrefix)\(key)"
}
func addAccessGroupWhenPresent(_ items: [String: Any]) -> [String: Any] {
guard let accessGroup = accessGroup else { return items }
var result: [String: Any] = items
result[KeychainSwiftConstants.accessGroup] = accessGroup
return result
}
/**
Adds kSecAttrSynchronizable: kSecAttrSynchronizableAny` item to the dictionary when the `synchronizable` property is true.
- parameter items: The dictionary where the kSecAttrSynchronizable items will be added when requested.
- parameter addingItems: Use `true` when the dictionary will be used with `SecItemAdd` method (adding a keychain item). For getting and deleting items, use `false`.
- returns: the dictionary with kSecAttrSynchronizable item added if it was requested. Otherwise, it returns the original dictionary.
*/
func addSynchronizableIfRequired(_ items: [String: Any], addingItems: Bool) -> [String: Any] {
if !synchronizable { return items }
var result: [String: Any] = items
result[KeychainSwiftConstants.attrSynchronizable] = addingItems == true ? true : kSecAttrSynchronizableAny
return result
}
}
// ----------------------------
//
// TegKeychainConstants.swift
//
// ----------------------------
import Foundation
import Security
/// Constants used by the library
public struct KeychainSwiftConstants {
/// Specifies a Keychain access group. Used for sharing Keychain items between apps.
public static var accessGroup: String { return toString(kSecAttrAccessGroup) }
/**
A value that indicates when your app needs access to the data in a keychain item. The default value is AccessibleWhenUnlocked. For a list of possible values, see KeychainSwiftAccessOptions.
*/
public static var accessible: String { return toString(kSecAttrAccessible) }
/// Used for specifying a String key when setting/getting a Keychain value.
public static var attrAccount: String { return toString(kSecAttrAccount) }
/// Used for specifying synchronization of keychain items between devices.
public static var attrSynchronizable: String { return toString(kSecAttrSynchronizable) }
/// An item class key used to construct a Keychain search dictionary.
public static var klass: String { return toString(kSecClass) }
/// Specifies the number of values returned from the keychain. The library only supports single values.
public static var matchLimit: String { return toString(kSecMatchLimit) }
/// A return data type used to get the data from the Keychain.
public static var returnData: String { return toString(kSecReturnData) }
/// Used for specifying a value when setting a Keychain value.
public static var valueData: String { return toString(kSecValueData) }
static func toString(_ value: CFString) -> String {
return value as String
}
}
// ----------------------------
//
// KeychainSwiftAccessOptions.swift
//
// ----------------------------
import Security
/**
These options are used to determine when a keychain item should be readable. The default value is AccessibleWhenUnlocked.
*/
public enum KeychainSwiftAccessOptions {
/**
The data in the keychain item can be accessed only while the device is unlocked by the user.
This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups.
This is the default value for keychain items added without explicitly setting an accessibility constant.
*/
case accessibleWhenUnlocked
/**
The data in the keychain item can be accessed only while the device is unlocked by the user.
This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
*/
case accessibleWhenUnlockedThisDeviceOnly
/**
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
*/
case accessibleAfterFirstUnlock
/**
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
*/
case accessibleAfterFirstUnlockThisDeviceOnly
/**
The data in the keychain item can always be accessed regardless of whether the device is locked.
This is not recommended for application use. Items with this attribute migrate to a new device when using encrypted backups.
*/
case accessibleAlways
/**
The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device.
This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted.
*/
case accessibleWhenPasscodeSetThisDeviceOnly
/**
The data in the keychain item can always be accessed regardless of whether the device is locked.
This is not recommended for application use. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
*/
case accessibleAlwaysThisDeviceOnly
static var defaultOption: KeychainSwiftAccessOptions {
return .accessibleWhenUnlocked
}
var value: String {
switch self {
case .accessibleWhenUnlocked:
return toString(kSecAttrAccessibleWhenUnlocked)
case .accessibleWhenUnlockedThisDeviceOnly:
return toString(kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
case .accessibleAfterFirstUnlock:
return toString(kSecAttrAccessibleAfterFirstUnlock)
case .accessibleAfterFirstUnlockThisDeviceOnly:
return toString(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
case .accessibleAlways:
return toString(kSecAttrAccessibleAlways)
case .accessibleWhenPasscodeSetThisDeviceOnly:
return toString(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)
case .accessibleAlwaysThisDeviceOnly:
return toString(kSecAttrAccessibleAlwaysThisDeviceOnly)
}
}
func toString(_ value: CFString) -> String {
return KeychainSwiftConstants.toString(value)
}
}

127
ios/Podfile

@ -0,0 +1,127 @@
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
platform :ios, '10.0'
workspace 'BlueWallet'
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
puts "Setting Swift Version setting for #{target.name}..."
config.build_settings['SWIFT_VERSION'] = '4.2'
end
end
end
def sharedPods
# Explicitly include Yoga if you are using RN >= 0.42.0
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
# Third party deps podspec link
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'CxxBridge', # Include this for RN >= 0.47
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
'RCTText',
'RCTImage', # <-- Add RCTImage
'RCTNetwork',
'RCTActionSheet',
'RCTLinkingIOS',
'RCTWebSocket', # Needed for debugging
'RCTAnimation', # Needed for FlatList and animations running on native UI thread
# Add any other subspecs you want to use in your project
]
end
target 'BlueWallet' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
project 'BlueWallet.xcodeproj'
platform :ios, '10.0'
# Pods for BlueWallet
# React Native requirements
sharedPods
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
pod 'react-native-camera', :path => '../node_modules/react-native-camera'
pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'
pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler'
pod 'RNFS', :path => '../node_modules/react-native-fs'
pod 'react-native-google-analytics-bridge', :path => '../node_modules/react-native-google-analytics-bridge'
pod 'react-native-haptic-feedback', :path => '../node_modules/react-native-haptic-feedback'
pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'
pod 'RNRate', :path => '../node_modules/react-native-rate/ios'
pod 'react-native-image-picker', :path => '../node_modules/react-native-image-picker'
pod 'RNSVG', :path => '../node_modules/react-native-svg'
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
pod 'react-native-randombytes', :path => '../node_modules/react-native-randombytes'
pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider'
pod 'SentryReactNative', :path => '../node_modules/react-native-sentry'
pod 'ToolTipMenu', :path => '../node_modules/react-native-tooltip'
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
end
target 'BlueWalletTests' do
inherit! :search_paths
# Pods for testing
end
target 'BlueWalletWatch' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
# use_frameworks!
# Pods for BlueWalletWatch
platform :watchos, '5.1'
end
target 'BlueWalletWatch Extension' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
# use_frameworks!
platform :watchos, '5.1'
pod 'EFQRCode', '~> 5.0.0'
# Pods for BlueWalletWatch Extension
end
target 'TcpSockets' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
# use_frameworks!
project '../node_modules/react-native-tcp/ios/TcpSockets.xcodeproj'
sharedPods
end
target 'RCTQRCodeLocalImage' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
# use_frameworks!
project '../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage.xcodeproj'
sharedPods
end
target 'RCTPrivacySnapshot' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
# use_frameworks!
project '../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot.xcodeproj'
sharedPods
end

232
ios/Podfile.lock

@ -0,0 +1,232 @@
PODS:
- boost-for-react-native (1.63.0)
- BVLinearGradient (2.5.4):
- React
- DoubleConversion (1.1.6)
- EFQRCode (5.0.0):
- swift_qrcodejs (~> 1.1.1)
- Folly (2018.10.22.00):
- boost-for-react-native
- DoubleConversion
- glog
- glog (0.3.5)
- React (0.59.6):
- React/Core (= 0.59.6)
- react-native-camera (2.6.0):
- React
- react-native-camera/RCT (= 2.6.0)
- react-native-camera/RN (= 2.6.0)
- react-native-camera/RCT (2.6.0):
- React
- react-native-camera/RN (2.6.0):
- React
- react-native-google-analytics-bridge (7.1.0):
- react-native-google-analytics-bridge/Core (= 7.1.0)
- react-native-google-analytics-bridge/Core (7.1.0):
- React
- react-native-haptic-feedback (1.7.1):
- React
- react-native-image-picker (0.28.1):
- React
- react-native-randombytes (3.5.2):
- React
- react-native-slider (1.1.0):
- React
- react-native-webview (5.8.1):
- React
- React/Core (0.59.6):
- yoga (= 0.59.6.React)
- React/CxxBridge (0.59.6):
- Folly (= 2018.10.22.00)
- React/Core
- React/cxxreact
- React/jsiexecutor
- React/cxxreact (0.59.6):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React/jsinspector
- React/DevSupport (0.59.6):
- React/Core
- React/RCTWebSocket
- React/fishhook (0.59.6)
- React/jsi (0.59.6):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React/jsiexecutor (0.59.6):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React/cxxreact
- React/jsi
- React/jsinspector (0.59.6)
- React/RCTActionSheet (0.59.6):
- React/Core
- React/RCTAnimation (0.59.6):
- React/Core
- React/RCTBlob (0.59.6):
- React/Core
- React/RCTImage (0.59.6):
- React/Core
- React/RCTNetwork
- React/RCTLinkingIOS (0.59.6):
- React/Core
- React/RCTNetwork (0.59.6):
- React/Core
- React/RCTText (0.59.6):
- React/Core
- React/RCTWebSocket (0.59.6):
- React/Core
- React/fishhook
- React/RCTBlob
- RNCAsyncStorage (1.3.3):
- React
- RNDeviceInfo (1.6.0):
- React
- RNFS (2.13.3):
- React
- RNGestureHandler (1.2.0):
- React
- RNRate (1.0.1):
- React
- RNSVG (9.4.0):
- React
- RNVectorIcons (6.4.2):
- React
- RNWatch (0.2.0):
- React
- Sentry (4.1.3):
- Sentry/Core (= 4.1.3)
- Sentry/Core (4.1.3)
- SentryReactNative (0.42.0):
- React
- Sentry (~> 4.1.3)
- swift_qrcodejs (1.1.1)
- ToolTipMenu (5.2.1):
- React
- yoga (0.59.6.React)
DEPENDENCIES:
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EFQRCode (~> 5.0.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- react-native-camera (from `../node_modules/react-native-camera`)
- react-native-google-analytics-bridge (from `../node_modules/react-native-google-analytics-bridge`)
- react-native-haptic-feedback (from `../node_modules/react-native-haptic-feedback`)
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- react-native-webview (from `../node_modules/react-native-webview`)
- React/Core (from `../node_modules/react-native`)
- React/CxxBridge (from `../node_modules/react-native`)
- React/DevSupport (from `../node_modules/react-native`)
- React/RCTActionSheet (from `../node_modules/react-native`)
- React/RCTAnimation (from `../node_modules/react-native`)
- React/RCTImage (from `../node_modules/react-native`)
- React/RCTLinkingIOS (from `../node_modules/react-native`)
- React/RCTNetwork (from `../node_modules/react-native`)
- React/RCTText (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNRate (from `../node_modules/react-native-rate/ios`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- RNWatch (from `../node_modules/react-native-watch-connectivity`)
- SentryReactNative (from `../node_modules/react-native-sentry`)
- ToolTipMenu (from `../node_modules/react-native-tooltip`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- boost-for-react-native
- EFQRCode
- Sentry
- swift_qrcodejs
EXTERNAL SOURCES:
BVLinearGradient:
:path: "../node_modules/react-native-linear-gradient"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
React:
:path: "../node_modules/react-native"
react-native-camera:
:path: "../node_modules/react-native-camera"
react-native-google-analytics-bridge:
:path: "../node_modules/react-native-google-analytics-bridge"
react-native-haptic-feedback:
:path: "../node_modules/react-native-haptic-feedback"
react-native-image-picker:
:path: "../node_modules/react-native-image-picker"
react-native-randombytes:
:path: "../node_modules/react-native-randombytes"
react-native-slider:
:path: "../node_modules/@react-native-community/slider"
react-native-webview:
:path: "../node_modules/react-native-webview"
RNCAsyncStorage:
:path: "../node_modules/@react-native-community/async-storage"
RNDeviceInfo:
:path: "../node_modules/react-native-device-info"
RNFS:
:path: "../node_modules/react-native-fs"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNRate:
:path: "../node_modules/react-native-rate/ios"
RNSVG:
:path: "../node_modules/react-native-svg"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
RNWatch:
:path: "../node_modules/react-native-watch-connectivity"
SentryReactNative:
:path: "../node_modules/react-native-sentry"
ToolTipMenu:
:path: "../node_modules/react-native-tooltip"
yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
BVLinearGradient: b0b70acf63ee888829b7c2ebbf6b50e227396e55
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
EFQRCode: 07437cfbce3a1e497397a4f3d766c980d8972608
Folly: de497beb10f102453a1afa9edbf8cf8a251890de
glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d
React: 1d605e098d69bdf08960787f3446f0a9dc2e2ccf
react-native-camera: 9c50d7def800895e7991ccda6203929553ceec9c
react-native-google-analytics-bridge: 0a86be2860b81a3562fe60ac40c0ad732340046f
react-native-haptic-feedback: f675486e3889e3229272158943c1e9e075247e5a
react-native-image-picker: f42de90075c5b1af53417af927631d909a1a746e
react-native-randombytes: d3184d351604f78e019535178766590188bbc133
react-native-slider: 743940825f1fa1b37e8396ffd8cebe41f4967e1f
react-native-webview: a42108b827082f8f0333529b0772102031d5960d
RNCAsyncStorage: 289488409d0c42f30e12535e3f45c5bd3cfc73d2
RNDeviceInfo: 08dd79c5adef48b6dc103bf3ddf208039aa78664
RNFS: bbb1a64eb245763daf34aea86f97c97c4e85f74c
RNGestureHandler: 7ccf2f3f60458e084f9ada01fbaf610f6fef073c
RNRate: 72b5c9c2e62de9a01710918eb83d75fb99b44c7b
RNSVG: 4834be1d644eb77f0e3f6de851881b83758a3124
RNVectorIcons: 8c52e1e8da1153613fdef44748e865c25556cb9c
RNWatch: 394c44f35352309ab414daaadfa3c55a4a5224ee
Sentry: 4e8a17b61ddd116f89536cc81d567fdee1ebca96
SentryReactNative: fc630be25b30c1a494b478ba1fa38f761cc6da20
swift_qrcodejs: 0bacbfe321a99954c7b8e04c75562007ea4e4f7c
ToolTipMenu: a01f5df49eb1a1ffbc5e1e81d2ec42b832436421
yoga: 128daf064cacaede0c3bb27424b6b4c71052e6cd
PODFILE CHECKSUM: 40fe32f25e14511848fc633565a8030139a972fa
COCOAPODS: 1.5.3

26
ios/Pods/Local Podspecs/RNDeviceInfo.podspec.json

@ -0,0 +1,26 @@
{
"name": "RNDeviceInfo",
"version": "1.6.0",
"summary": "Get device information using react-native",
"license": "MIT",
"authors": {
"name": "Rebecca Hughes",
"email": "rebecca@learnium.net",
"url": "https://github.com/rebeccahughes"
},
"homepage": "git+https://github.com/react-native-community/react-native-device-info.git",
"platforms": {
"ios": "9.0",
"tvos": "10.0"
},
"source": {
"git": "https://github.com/react-native-community/react-native-device-info.git",
"tag": "1.6.0"
},
"source_files": "ios/**/*.{h,m}",
"dependencies": {
"React": [
]
}
}

23
ios/Pods/Local Podspecs/RNSVG.podspec.json

@ -0,0 +1,23 @@
{
"name": "RNSVG",
"version": "9.4.0",
"summary": "SVG library for react-native",
"license": "MIT",
"homepage": "https://github.com/react-native-community/react-native-svg",
"authors": "Horcrux Chen",
"source": {
"git": "https://github.com/react-native-community/react-native-svg.git",
"tag": "9.4.0"
},
"source_files": "ios/**/*.{h,m}",
"requires_arc": true,
"platforms": {
"ios": "8.0",
"tvos": "9.2"
},
"dependencies": {
"React": [
]
}
}

565
ios/Pods/Local Podspecs/React.podspec.json

@ -0,0 +1,565 @@
{
"name": "React",
"version": "0.59.6",
"summary": "A framework for building native apps using React",
"description": "React Native apps are built using the React JS\nframework, and render directly to native UIKit\nelements using a fully asynchronous architecture.\nThere is no browser and no HTML. We have picked what\nwe think is the best set of features from these and\nother technologies to build what we hope to become\nthe best product development framework available,\nwith an emphasis on iteration speed, developer\ndelight, continuity of technology, and absolutely\nbeautiful and fast products with no compromises in\nquality or capability.",
"homepage": "http://facebook.github.io/react-native/",
"license": "MIT",
"authors": "Facebook",
"source": {
"git": "https://github.com/facebook/react-native.git",
"tag": "v0.59.6"
},
"default_subspecs": "Core",
"requires_arc": true,
"platforms": {
"ios": "9.0",
"tvos": "9.2"
},
"pod_target_xcconfig": {
"CLANG_CXX_LANGUAGE_STANDARD": "c++14"
},
"preserve_paths": [
"package.json",
"LICENSE",
"LICENSE-docs"
],
"cocoapods_version": ">= 1.2.0",
"subspecs": [
{
"name": "Core",
"dependencies": {
"yoga": [
"0.59.6.React"
]
},
"source_files": "React/**/*.{c,h,m,mm,S,cpp}",
"exclude_files": [
"**/__tests__/*",
"IntegrationTests/*",
"React/DevSupport/*",
"React/Inspector/*",
"ReactCommon/yoga/*",
"React/Cxx*/*",
"React/Fabric/**/*"
],
"ios": {
"exclude_files": "React/**/RCTTV*.*"
},
"tvos": {
"exclude_files": [
"React/Modules/RCTClipboard*",
"React/Views/RCTDatePicker*",
"React/Views/RCTPicker*",
"React/Views/RCTRefreshControl*",
"React/Views/RCTSlider*",
"React/Views/RCTSwitch*",
"React/Views/RCTWebView*"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"header_dir": "React",
"frameworks": "JavaScriptCore",
"libraries": "stdc++",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\""
}
},
{
"name": "CxxBridge",
"dependencies": {
"Folly": [
"2018.10.22.00"
],
"React/Core": [
],
"React/cxxreact": [
],
"React/jsiexecutor": [
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"private_header_files": "React/Cxx*/*.h",
"source_files": "React/Cxx*/*.{h,m,mm}"
},
{
"name": "DevSupport",
"dependencies": {
"React/Core": [
],
"React/RCTWebSocket": [
]
},
"source_files": [
"React/DevSupport/*",
"React/Inspector/*"
]
},
{
"name": "RCTFabric",
"dependencies": {
"Folly": [
"2018.10.22.00"
],
"React/Core": [
],
"React/fabric": [
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "React/Fabric/**/*.{c,h,m,mm,S,cpp}",
"exclude_files": "**/tests/*",
"header_dir": "React",
"frameworks": "JavaScriptCore",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\""
}
},
{
"name": "tvOS",
"dependencies": {
"React/Core": [
]
},
"source_files": "React/**/RCTTV*.{h,m}"
},
{
"name": "jsinspector",
"source_files": "ReactCommon/jsinspector/*.{cpp,h}",
"private_header_files": "ReactCommon/jsinspector/*.h",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\""
}
},
{
"name": "jsiexecutor",
"dependencies": {
"React/cxxreact": [
],
"React/jsi": [
],
"Folly": [
"2018.10.22.00"
],
"DoubleConversion": [
],
"glog": [
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/jsiexecutor/jsireact/*.{cpp,h}",
"private_header_files": "ReactCommon/jsiexecutor/jsireact/*.h",
"header_dir": "jsireact",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\", \"$(PODS_TARGET_SRCROOT)/ReactCommon/jsiexecutor\""
}
},
{
"name": "jsi",
"dependencies": {
"Folly": [
"2018.10.22.00"
],
"DoubleConversion": [
],
"glog": [
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/jsi/*.{cpp,h}",
"private_header_files": "ReactCommon/jsi/*.h",
"frameworks": "JavaScriptCore",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\""
}
},
{
"name": "PrivateDatabase",
"source_files": "ReactCommon/privatedata/*.{cpp,h}",
"private_header_files": "ReactCommon/privatedata/*.h",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\""
}
},
{
"name": "cxxreact",
"dependencies": {
"React/jsinspector": [
],
"boost-for-react-native": [
"1.63.0"
],
"Folly": [
"2018.10.22.00"
],
"DoubleConversion": [
],
"glog": [
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/cxxreact/*.{cpp,h}",
"exclude_files": "ReactCommon/cxxreact/SampleCxxModule.*",
"private_header_files": "ReactCommon/cxxreact/*.h",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "fabric",
"subspecs": [
{
"name": "activityindicator",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/activityindicator/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/activityindicator",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "attributedstring",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/attributedstring/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/attributedstring",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "core",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/core/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/core",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "debug",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/debug/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/debug",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "graphics",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/graphics/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/graphics",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "scrollview",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/scrollview/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/scrollview",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "text",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/text/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/text",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "textlayoutmanager",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/textlayoutmanager/**/*.{cpp,h,mm}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/textlayoutmanager",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "uimanager",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/uimanager/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/uimanager",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "view",
"dependencies": {
"Folly": [
"2018.10.22.00"
],
"yoga": [
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/view/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/view",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
}
]
},
{
"name": "RCTFabricSample",
"dependencies": {
"Folly": [
"2018.10.22.00"
]
},
"compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"source_files": "ReactCommon/fabric/sample/**/*.{cpp,h}",
"exclude_files": "**/tests/*",
"header_dir": "fabric/sample",
"pod_target_xcconfig": {
"HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\""
}
},
{
"name": "ART",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/ART/**/*.{h,m}"
},
{
"name": "RCTActionSheet",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/ActionSheetIOS/*.{h,m}"
},
{
"name": "RCTAnimation",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/NativeAnimation/{Drivers/*,Nodes/*,*}.{h,m}",
"header_dir": "RCTAnimation"
},
{
"name": "RCTBlob",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/Blob/*.{h,m,mm}",
"preserve_paths": "Libraries/Blob/*.js"
},
{
"name": "RCTCameraRoll",
"dependencies": {
"React/Core": [
],
"React/RCTImage": [
]
},
"source_files": "Libraries/CameraRoll/*.{h,m}"
},
{
"name": "RCTGeolocation",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/Geolocation/*.{h,m}"
},
{
"name": "RCTImage",
"dependencies": {
"React/Core": [
],
"React/RCTNetwork": [
]
},
"source_files": "Libraries/Image/*.{h,m}"
},
{
"name": "RCTNetwork",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/Network/*.{h,m,mm}"
},
{
"name": "RCTPushNotification",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/PushNotificationIOS/*.{h,m}"
},
{
"name": "RCTSettings",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/Settings/*.{h,m}"
},
{
"name": "RCTText",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/Text/**/*.{h,m}"
},
{
"name": "RCTVibration",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/Vibration/*.{h,m}"
},
{
"name": "RCTWebSocket",
"dependencies": {
"React/Core": [
],
"React/RCTBlob": [
],
"React/fishhook": [
]
},
"source_files": "Libraries/WebSocket/*.{h,m}"
},
{
"name": "fishhook",
"header_dir": "fishhook",
"source_files": "Libraries/fishhook/*.{h,c}"
},
{
"name": "RCTLinkingIOS",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/LinkingIOS/*.{h,m}"
},
{
"name": "RCTTest",
"dependencies": {
"React/Core": [
]
},
"source_files": "Libraries/RCTTest/**/*.{h,m}",
"frameworks": "XCTest"
},
{
"name": "_ignore_me_subspec_for_linting_",
"dependencies": {
"React/Core": [
],
"React/CxxBridge": [
]
}
}
]
}

97
ios/Pods/Local Podspecs/react-native-camera.podspec.json

@ -0,0 +1,97 @@
{
"name": "react-native-camera",
"version": "2.6.0",
"summary": "A Camera component for React Native. Also reads barcodes.",
"description": "A Camera component for React Native. Also reads barcodes.",
"license": "MIT AND Apache-2.0 AND BSD-3-Clause",
"authors": {
"name": "Lochlan Wansbrough",
"email": "lochie@live.com",
"url": "http://lwansbrough.com"
},
"homepage": "https://github.com/react-native-community/react-native-camera",
"source": {
"git": "https://github.com/react-native-community/react-native-camera",
"tag": "v2.6.0"
},
"requires_arc": true,
"platforms": {
"ios": "9.0"
},
"default_subspecs": [
"RN",
"RCT"
],
"preserve_paths": [
"LICENSE",
"README.md",
"package.json",
"index.js"
],
"dependencies": {
"React": [
]
},
"subspecs": [
{
"name": "RCT",
"source_files": "ios/RCT/**/*.{h,m}"
},
{
"name": "RN",
"source_files": "ios/RN/**/*.{h,m}"
},
{
"name": "TextDetector",
"dependencies": {
"react-native-camera/RN": [
],
"react-native-camera/RCT": [
],
"Firebase/MLVision": [
],
"Firebase/MLVisionTextModel": [
]
}
},
{
"name": "FaceDetectorMLKit",
"dependencies": {
"react-native-camera/RN": [
],
"react-native-camera/RCT": [
],
"Firebase/MLVision": [
],
"Firebase/MLVisionFaceModel": [
]
}
},
{
"name": "BarcodeDetectorMLKit",
"dependencies": {
"react-native-camera/RN": [
],
"react-native-camera/RCT": [
],
"Firebase/MLVision": [
],
"Firebase/MLVisionBarcodeModel": [
]
}
}
]
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save