├── .babelrc
├── .gitignore
├── LICENSE.md
├── README.md
├── app
├── actions
│ ├── AccountActions.js
│ ├── AssetActions.js
│ ├── BackupActions.js
│ ├── BalanceClaimActiveActions.js
│ ├── CachedPropertyActions.js
│ ├── IntlActions.js
│ ├── MarketsActions.js
│ ├── NotificationActions.js
│ ├── PrivateKeyActions.js
│ ├── ScanActions.js
│ ├── SettingsActions.js
│ ├── TransactionConfirmActions.js
│ ├── WalletActions.js
│ ├── WalletUnlockActions.js
│ └── layout
│ │ └── ConfirmActions.js
├── api
│ ├── ApplicationApi.js
│ ├── WalletApi.js
│ └── accountApi.js
├── app.js
├── assets
│ ├── fonts
│ │ └── WebSymbols-Regular.otf
│ ├── imgs
│ │ ├── close.png
│ │ ├── open.png
│ │ └── symbols
│ │ │ ├── bkt.png
│ │ │ ├── blockpay.png
│ │ │ ├── btc.png
│ │ │ ├── bts.png
│ │ │ ├── btsr.png
│ │ │ ├── btwty.png
│ │ │ ├── cny.png
│ │ │ ├── dao.png
│ │ │ ├── dash.png
│ │ │ ├── dct.png
│ │ │ ├── dgd.png
│ │ │ ├── eth.png
│ │ │ ├── eur.png
│ │ │ ├── eurt.png
│ │ │ ├── game.png
│ │ │ ├── gold.png
│ │ │ ├── grc.png
│ │ │ ├── hempsweet.png
│ │ │ ├── icoo.png
│ │ │ ├── incnt.png
│ │ │ ├── lisk.png
│ │ │ ├── mkr.png
│ │ │ ├── nxc.png
│ │ │ ├── obits.png
│ │ │ ├── open.btc.png
│ │ │ ├── peerplays.png
│ │ │ ├── steem.png
│ │ │ ├── usd.png
│ │ │ └── usdt.png
│ ├── loader.js
│ ├── locales
│ │ ├── locale-en.js
│ │ └── locale-zh.js
│ └── styles
│ │ ├── app.scss
│ │ ├── define.scss
│ │ ├── flip.scss
│ │ ├── modal.scss
│ │ └── symbols.scss
├── components
│ ├── BaseComponent.js
│ ├── Blockchain
│ │ ├── BlockTime.js
│ │ ├── MemoInfo.js
│ │ └── Transaction.js
│ ├── GlobalSetting.js
│ ├── LastOperation.js
│ ├── Loading.js
│ ├── NavigationBar.js
│ ├── PopupMenu.js
│ ├── Root.js
│ ├── RootIntl.js
│ ├── Settings.js
│ ├── TextLoading.js
│ ├── TransactionConfirm.js
│ ├── Utility
│ │ ├── AccountImage.js
│ │ ├── AccountName.js
│ │ ├── AssetName.js
│ │ ├── BalanceComponent.js
│ │ ├── BindToChainState.js
│ │ ├── ChainTypes.js
│ │ ├── FormattedAsset.js
│ │ ├── FormattedPrice.js
│ │ ├── Identicon.js
│ │ ├── PriceText.js
│ │ ├── TimeAgo.js
│ │ ├── TotalBalanceValue.js
│ │ ├── ValueComponent.js
│ │ └── intlData.js
│ ├── containers
│ │ └── GlobalSettingContainer.js
│ ├── dashboard
│ │ ├── AccountList.js
│ │ ├── AssetsItem.js
│ │ ├── Balance.js
│ │ ├── Dashboard.js
│ │ └── Operation.js
│ ├── form
│ │ ├── XNFullButton.js
│ │ ├── XNFullText.js
│ │ ├── XNSelect.js
│ │ └── XNSwitch.js
│ ├── layout
│ │ ├── Confirm.js
│ │ └── Modal.js
│ ├── scanit
│ │ └── Scan.js
│ ├── transaction
│ │ ├── Buy.js
│ │ ├── CanBuySell.js
│ │ ├── CurrentBalance.js
│ │ ├── History.js
│ │ ├── MarketList.js
│ │ ├── OrderBook.js
│ │ ├── Orders.js
│ │ ├── Sell.js
│ │ ├── TabComponent.js
│ │ ├── Transaction.js
│ │ ├── TransactionContainer.js
│ │ └── TransactionOperation.js
│ └── wallet
│ │ ├── AccountNameInput.js
│ │ ├── AccountSelectInput.js
│ │ ├── AmountSelectInput.js
│ │ ├── Backup.js
│ │ ├── ChangePassword.js
│ │ ├── CreateAccount.js
│ │ ├── ImportBackup.js
│ │ ├── ImportKey.js
│ │ ├── KeysView.js
│ │ ├── PasswordInput.js
│ │ ├── Transfer.js
│ │ ├── UnlockWallet.js
│ │ └── WalletManage.js
├── idb-helper.js
├── idb-instance.js
├── idb-root.js
├── main.js
├── stores
│ ├── AccountRefsStore.js
│ ├── AccountStore.js
│ ├── AddressIndex.js
│ ├── AssetStore.js
│ ├── BackupStore.js
│ ├── BalanceClaimActiveStore.js
│ ├── BaseStore.js
│ ├── CachedPropertyStore.js
│ ├── ImportKeysStore.js
│ ├── IntlStore.js
│ ├── MarketsStore.js
│ ├── NotificationStore.js
│ ├── PrivateKeyStore.js
│ ├── ScanStore.js
│ ├── SettingsStore.js
│ ├── TransactionConfirmStore.js
│ ├── WalletDb.js
│ ├── WalletManagerStore.js
│ ├── WalletUnlockStore.js
│ ├── layout
│ │ └── ConfirmStore.js
│ └── tcomb_structs.js
└── workers
│ ├── AddressIndexWorker.js
│ ├── AesWorker.js
│ └── GenesisFilterWorker.js
├── build
├── .gitignore
├── favicon.ico
└── index.html
├── common
├── GenesisFilter.js
├── MarketClasses.js
├── account_constants.js
├── account_utils.js
├── altObj.js
├── asset_constants.js
├── asset_utils.js
├── dictionary_en.json
├── localStorage.js
├── localStorageImpl.js
├── market_utils.js
└── utils.js
├── cordova
├── config.xml
├── release.bat
├── res
│ ├── icon
│ │ └── android
│ │ │ ├── drawable-hdpi
│ │ │ └── icon.png
│ │ │ ├── drawable-ldpi
│ │ │ └── icon.png
│ │ │ ├── drawable-mdpi
│ │ │ ├── bts.png
│ │ │ └── icon.png
│ │ │ └── drawable-xhdpi
│ │ │ └── icon.png
│ └── screen
│ │ └── android
│ │ ├── splash-land-hdpi.png
│ │ ├── splash-land-ldpi.png
│ │ ├── splash-land-mdpi.png
│ │ ├── splash-land-xhdpi.png
│ │ ├── splash-port-hdpi.png
│ │ ├── splash-port-ldpi.png
│ │ ├── splash-port-mdpi.png
│ │ └── splash-port-xhdpi.png
└── www
│ └── index.html
├── package.json
├── webpack.config.js
└── webpack.pro.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react",
5 | "stage-0"
6 | ],
7 | "ignore": [
8 | "./common/dictionary_en.json"
9 | ],
10 | "plugins": []
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (C) 2017 necklace,and contributors.
2 |
3 | The MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # btsgo
2 | bitshares2网页钱包项目
3 | 项目基于bitsharesjs
4 | 开发环境:
5 | 1.Python27 编译node-sass时会用到
6 | 2.Git 用来下载github上的源码
7 | 3.WebStorm 代码编辑IDE
8 | 4.react+webpack
9 |
10 | ## Web版本地址:https://btsgo.net
11 |
12 | # 欢迎打赏BTS:xiangxn
13 | # 欢迎支持见证人:xn-delegate
14 |
--------------------------------------------------------------------------------
/app/actions/AccountActions.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import accountUtils from "../../common/account_utils";
3 | import AccountApi from "../api/accountApi";
4 |
5 | import WalletApi from "../api/WalletApi";
6 | import ApplicationApi from "../api/ApplicationApi";
7 | import WalletDb from "../stores/WalletDb";
8 | import WalletActions from "./WalletActions";
9 |
10 | let accountSubs = {};
11 | let accountLookup = {};
12 | let accountSearch = {};
13 | let wallet_api = new WalletApi();
14 | let application_api = new ApplicationApi()
15 | let inProgress = {};
16 |
17 | /**
18 | * @brief Actions that modify linked accounts
19 | *
20 | * @note this class also includes accountSearch actions which keep track of search result state. The presumption
21 | * is that there is only ever one active "search result" at a time.
22 | */
23 | class AccountActions {
24 |
25 | /**
26 | * Account search results are not managed by the ChainStore cache so are
27 | * tracked as part of the AccountStore.
28 | */
29 | accountSearch(start_symbol, limit = 50) {
30 | let uid = `${start_symbol}_${limit}}`;
31 | return (dispatch) => {
32 | if (!accountSearch[uid]) {
33 | accountSearch[uid] = true;
34 | return AccountApi.lookupAccounts(start_symbol, limit)
35 | .then(result => {
36 | accountSearch[uid] = false;
37 | dispatch({accounts: result, searchTerm: start_symbol});
38 | });
39 | }
40 | };
41 | }
42 |
43 | /**
44 | * TODO: The concept of current accounts is deprecated and needs to be removed
45 | */
46 | setCurrentAccount(name) {
47 | return name;
48 | }
49 |
50 | /**
51 | * TODO: This is a function of teh wallet_api and has no business being part of AccountActions
52 | */
53 | transfer(from_account, to_account, amount, asset, memo, propose_account = null, fee_asset_id = "1.3.0") {
54 | // Set the fee asset to use
55 | fee_asset_id = accountUtils.getFinalFeeAsset(propose_account || from_account, "transfer", fee_asset_id);
56 | try {
57 | return (dispatch) => {
58 | return application_api.transfer({
59 | from_account, to_account, amount, asset, memo, propose_account, fee_asset_id
60 | }).then(result => {
61 | // console.log( "transfer result: ", result )
62 |
63 | dispatch(result);
64 | });
65 | };
66 | } catch (error) {
67 | console.log("[AccountActions.js:90] ----- transfer error ----->", error);
68 | return new Promise((resolve, reject) => {
69 | reject(error);
70 | });
71 | }
72 | }
73 |
74 | /**
75 | * This method exists ont he AccountActions because after creating the account via the wallet, the account needs
76 | * to be linked and added to the local database.
77 | */
78 | createAccount(
79 | account_name,
80 | registrar,
81 | referrer,
82 | referrer_percent,
83 | refcode
84 | ) {
85 | return (dispatch) => {
86 | return WalletActions.createAccount(
87 | account_name,
88 | registrar,
89 | referrer,
90 | referrer_percent,
91 | refcode
92 | ).then( () => {
93 | dispatch(account_name);
94 | return account_name;
95 | });
96 | };
97 | }
98 |
99 | /**
100 | * TODO: This is a function of the wallet_api and has no business being part of AccountActions, the account should already
101 | * be linked.
102 | */
103 | upgradeAccount(account_id, lifetime) {
104 | // Set the fee asset to use
105 | let fee_asset_id = accountUtils.getFinalFeeAsset(account_id, "account_upgrade");
106 |
107 | var tr = wallet_api.new_transaction();
108 | tr.add_type_operation("account_upgrade", {
109 | "fee": {
110 | amount: 0,
111 | asset_id: fee_asset_id
112 | },
113 | "account_to_upgrade": account_id,
114 | "upgrade_to_lifetime_member": lifetime
115 | });
116 | return WalletDb.process_transaction(tr, null, true);
117 | }
118 |
119 | linkAccount(name) {
120 | return name;
121 | }
122 |
123 | unlinkAccount(name) {
124 | return name;
125 | }
126 | }
127 |
128 | export default alt.createActions(AccountActions);
129 |
--------------------------------------------------------------------------------
/app/actions/BackupActions.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import iDB from "../idb-instance";
3 | import {compress, decompress} from "lzma";
4 | import {PrivateKey, PublicKey, Aes, key} from "bitsharesjs";
5 | import WalletActions from "./WalletActions";
6 |
7 | class BackupActions {
8 |
9 | incommingWebFile(file) {
10 | return (dispatch) => {
11 | let reader = new FileReader();
12 | reader.onload = evt => {
13 | let contents = new Buffer(evt.target.result, "binary");
14 | let name = file.name;
15 | let last_modified = file.lastModifiedDate.toString();
16 |
17 | dispatch({name, contents, last_modified});
18 | };
19 | reader.readAsBinaryString(file);
20 | };
21 | }
22 |
23 | incommingBuffer(params) {
24 | return params;
25 | }
26 |
27 | reset() {
28 | return true;
29 | }
30 |
31 | }
32 |
33 | let BackupActionsWrapped = alt.createActions(BackupActions);
34 | export default BackupActionsWrapped;
35 |
36 | export function backup(backup_pubkey) {
37 | return new Promise( resolve => {
38 | resolve(createWalletObject().then( wallet_object => {
39 | let compression = 1;
40 | return createWalletBackup(backup_pubkey, wallet_object, compression);
41 | }));
42 | });
43 | }
44 |
45 | /** No click backup.. Works great, but not used (yet?) */
46 | // export function backupToBin(
47 | // backup_pubkey = WalletDb.getWallet().password_pubkey,
48 | // saveAsCallback = saveAs
49 | // ) {
50 | // backup(backup_pubkey).then( contents => {
51 | // let name = iDB.getCurrentWalletName() + ".bin"
52 | // let blob = new Blob([ contents ], {
53 | // type: "application/octet-stream; charset=us-ascii"})
54 | //
55 | // if(blob.size !== contents.length)
56 | // throw new Error("Invalid backup to download conversion")
57 | //
58 | // saveAsCallback(blob, name);
59 | // WalletActions.setBackupDate()
60 | // })
61 | // }
62 |
63 | export function restore(backup_wif, backup, wallet_name) {
64 | return new Promise( resolve => {
65 | resolve(decryptWalletBackup(backup_wif, backup).then( wallet_object => {
66 | return WalletActions.restore(wallet_name, wallet_object);
67 | }));
68 | });
69 | }
70 |
71 | export function createWalletObject() {
72 | return iDB.backup();
73 | }
74 |
75 | /**
76 | compression_mode can be 1-9 (1 is fast and pretty good; 9 is slower and probably much better)
77 | */
78 | export function createWalletBackup(
79 | backup_pubkey, wallet_object, compression_mode, entropy) {
80 | return new Promise( resolve => {
81 | let public_key = PublicKey.fromPublicKeyString(backup_pubkey);
82 | let onetime_private_key = key.get_random_key(entropy);
83 | let walletString = JSON.stringify(wallet_object, null, 0);
84 | compress(walletString, compression_mode, compressedWalletBytes => {
85 | let backup_buffer =
86 | Aes.encrypt_with_checksum(onetime_private_key, public_key,
87 | null/*nonce*/, compressedWalletBytes);
88 |
89 | let onetime_public_key = onetime_private_key.toPublicKey();
90 | let backup = Buffer.concat([ onetime_public_key.toBuffer(), backup_buffer ]);
91 | resolve(backup);
92 | });
93 | });
94 | }
95 |
96 | export function decryptWalletBackup(backup_wif, backup_buffer) {
97 | return new Promise( (resolve, reject) => {
98 | if( ! Buffer.isBuffer(backup_buffer))
99 | backup_buffer = new Buffer(backup_buffer, "binary");
100 |
101 | let private_key = PrivateKey.fromWif(backup_wif);
102 | let public_key;
103 | try {
104 | public_key = PublicKey.fromBuffer(backup_buffer.slice(0, 33));
105 | } catch(e) {
106 | console.error(e, e.stack);
107 | throw new Error("Invalid backup file");
108 | }
109 |
110 | backup_buffer = backup_buffer.slice(33);
111 | try {
112 | backup_buffer = Aes.decrypt_with_checksum(
113 | private_key, public_key, null/*nonce*/, backup_buffer);
114 | } catch(error) {
115 | console.error("Error decrypting wallet", error, error.stack);
116 | reject("invalid_decryption_key");
117 | return;
118 | }
119 |
120 | try {
121 | decompress(backup_buffer, wallet_string => {
122 | try {
123 | let wallet_object = JSON.parse(wallet_string);
124 | resolve(wallet_object);
125 | } catch(error) {
126 | if( ! wallet_string) wallet_string = "";
127 | console.error("Error parsing wallet json",
128 | wallet_string.substring(0,10)+ "...");
129 | reject("Error parsing wallet json");
130 | }
131 | });
132 | } catch(error) {
133 | console.error("Error decompressing wallet", error, error.stack);
134 | reject("Error decompressing wallet");
135 | return;
136 | }
137 | });
138 | }
139 |
--------------------------------------------------------------------------------
/app/actions/BalanceClaimActiveActions.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj"
2 |
3 | class BalanceClaimActiveActions {
4 |
5 | setPubkeys(pubkeys) {
6 | return pubkeys;
7 | }
8 |
9 | setSelectedBalanceClaims(selected_balances) {
10 | return selected_balances;
11 | }
12 |
13 | claimAccountChange(claim_account_name) {
14 | return claim_account_name;
15 | }
16 |
17 | }
18 |
19 | var BalanceClaimActiveActionsWrapped = alt.createActions(BalanceClaimActiveActions)
20 | export default BalanceClaimActiveActionsWrapped
21 |
--------------------------------------------------------------------------------
/app/actions/CachedPropertyActions.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 |
3 | class CachedPropertyActions {
4 |
5 | set(name, value) {
6 | return { name, value };
7 | }
8 |
9 | get(name) {
10 | return { name };
11 | }
12 |
13 | reset() {
14 | return null;
15 | }
16 |
17 | }
18 |
19 | var CachedPropertyActionsWrapped = alt.createActions(CachedPropertyActions)
20 | export default CachedPropertyActionsWrapped
--------------------------------------------------------------------------------
/app/actions/IntlActions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/27.
3 | */
4 | import alt from "../../common/altObj";
5 |
6 | class IntlActions {
7 | switchLocale(locale) {
8 | return {locale};
9 | }
10 |
11 | getLocale(locale) {
12 | return {locale};
13 | }
14 | }
15 |
16 | export default alt.createActions(IntlActions);
--------------------------------------------------------------------------------
/app/actions/NotificationActions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/1/27.
3 | */
4 |
5 | import alt from "../../common/altObj";
6 |
7 | class NotificationActions {
8 |
9 | addNotification(notification) {
10 | notification = normalize(notification);
11 | return notification;
12 | }
13 |
14 | success(notification) {
15 | notification = normalize(notification, "success");
16 | return notification;
17 | }
18 |
19 | error(notification) {
20 | notification = normalize(notification, "error");
21 | return notification;
22 | }
23 |
24 | warning(notification) {
25 | notification = normalize(notification, "warning");
26 | return notification;
27 | }
28 |
29 | info(notification) {
30 | notification = normalize(notification, "info");
31 | return notification;
32 | }
33 | }
34 |
35 | export default alt.createActions(NotificationActions);
36 |
37 | var normalize = (notification, level) => {
38 | if(typeof notification == "string")
39 | notification = {message: notification};
40 | if(level)
41 | notification.level = level;
42 | return notification;
43 | };
--------------------------------------------------------------------------------
/app/actions/PrivateKeyActions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/28.
3 | */
4 | import alt from '../../common/altObj';
5 |
6 | class PrivateKeyActions {
7 |
8 | addKey(private_key_object, transaction) {
9 | return (dispatch) => {
10 | return new Promise( resolve => {
11 | dispatch({private_key_object, transaction, resolve});
12 | });
13 | };
14 | }
15 |
16 | loadDbData() {
17 | return (dispatch) => {
18 | return new Promise( resolve => {
19 | dispatch(resolve);
20 | });
21 | };
22 | }
23 | }
24 | export default alt.createActions(PrivateKeyActions);
--------------------------------------------------------------------------------
/app/actions/ScanActions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/10/17.
3 | */
4 |
5 | import alt from "../../common/altObj";
6 |
7 | class ScanActions {
8 |
9 | /**
10 | * 开始扫码
11 | * @param routerState 当前路由状态uri
12 | */
13 | scan(routerState) {
14 | return routerState;
15 | }
16 |
17 | /**
18 | * 设置扫描结果
19 | * @param qrStr
20 | * @return {*}
21 | */
22 | setScanResult(qrStr) {
23 | return qrStr;
24 | }
25 |
26 | /**
27 | * 重置Scan状态;最好在使用完以后调用一下
28 | * @return {boolean}
29 | */
30 | reset() {
31 | return true;
32 | }
33 | }
34 |
35 | export default alt.createActions(ScanActions);
--------------------------------------------------------------------------------
/app/actions/SettingsActions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/12.
3 | */
4 | import alt from '../../common/altObj';
5 |
6 | class SettingsActions {
7 |
8 | //修改设置
9 | changeSetting(value) {
10 | return value;
11 | }
12 |
13 | //添加api服务器
14 | addWS(ws) {
15 | return ws;
16 | }
17 |
18 | //移除api服务器
19 | removeWS(index) {
20 | return index;
21 | }
22 |
23 | //修改语言
24 | switchLocale(locale) {
25 | return {locale};
26 | }
27 |
28 | //清除设置
29 | clearSettings() {
30 | return null;
31 | }
32 |
33 | //添加初始账号
34 | addStarAccount(account) {
35 | return account;
36 | }
37 |
38 | //删除初始账号
39 | removeStarAccount(account) {
40 | return account;
41 | }
42 |
43 | //改变资产方向
44 | changeMarketDirection(value) {
45 | return value;
46 | }
47 | }
48 | export default alt.createActions(SettingsActions);
--------------------------------------------------------------------------------
/app/actions/TransactionConfirmActions.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import {ChainConfig} from "bitsharesjs-ws";
3 |
4 | class TransactionConfirmActions {
5 |
6 | confirm(transaction) {
7 | return {transaction};
8 | }
9 |
10 | broadcast(transaction) {
11 | return (dispatch) => {
12 | dispatch({broadcasting: true});
13 |
14 | let broadcast_timeout = setTimeout(() => {
15 | this.error("Your transaction has expired without being confirmed, please try again later.");
16 | }, ChainConfig.expire_in_secs * 2000);
17 |
18 | transaction.broadcast(() => {
19 | dispatch({broadcasting: false, broadcast: true});
20 | }).then( (res)=> {
21 | clearTimeout(broadcast_timeout);
22 | dispatch({
23 | error: null,
24 | broadcasting: false,
25 | broadcast: true,
26 | included: true,
27 | trx_id: res[0].id,
28 | trx_block_num: res[0].block_num,
29 | broadcasted_transaction: true
30 | });
31 | }).catch( error => {
32 | console.error(error);
33 | clearTimeout(broadcast_timeout);
34 | // messages of length 1 are local exceptions (use the 1st line)
35 | // longer messages are remote API exceptions (use the 2nd line)
36 | let splitError = error.message.split( "\n" );
37 | let message = splitError[splitError.length === 1 ? 0 : 1];
38 | dispatch({
39 | broadcast: false,
40 | broadcasting: false,
41 | error: message
42 | });
43 | });
44 | };
45 | }
46 |
47 | wasBroadcast(res){
48 | return res;
49 | }
50 |
51 | wasIncluded(res){
52 | return res;
53 | }
54 |
55 | close() {
56 | return true;
57 | }
58 |
59 | error(msg) {
60 | return {error: msg};
61 | }
62 |
63 | togglePropose() {
64 | return true;
65 | }
66 |
67 | proposeFeePayingAccount(fee_paying_account) {
68 | return fee_paying_account;
69 | }
70 | }
71 |
72 | export default alt.createActions(TransactionConfirmActions);
73 |
--------------------------------------------------------------------------------
/app/actions/WalletUnlockActions.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 |
3 | class WalletUnlockActions {
4 |
5 | /** If you get resolved then the wallet is or was just unlocked. If you get
6 | rejected then the wallet is still locked.
7 |
8 | @return nothing .. Just test for resolve() or reject()
9 | */
10 | unlock() {
11 | return (dispatch) => {
12 | return new Promise( (resolve, reject) => {
13 | dispatch({resolve, reject});
14 | }).then( was_unlocked => {
15 | //console.log('... WalletUnlockStore\tmodal unlock')
16 | if(was_unlocked) WrappedWalletUnlockActions.change();
17 | });
18 | };
19 | }
20 |
21 | lock() {
22 | return (dispatch) => {
23 | return new Promise( resolve => {
24 | dispatch({resolve});
25 | }).then( was_unlocked => {
26 | if(was_unlocked) WrappedWalletUnlockActions.change();
27 | });
28 | };
29 | }
30 |
31 | cancel() {
32 | return true;
33 | }
34 |
35 | change() {
36 | return true;
37 | }
38 |
39 | }
40 |
41 | var WrappedWalletUnlockActions = alt.createActions(WalletUnlockActions)
42 | export default WrappedWalletUnlockActions
43 |
--------------------------------------------------------------------------------
/app/actions/layout/ConfirmActions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/3/17.
3 | */
4 | import alt from "../../../common/altObj";
5 |
6 | class ConfirmActions {
7 | show(title, msg, onok, oncancel, height, nocancel) {
8 |
9 | return {title, msg, onok, oncancel, height, nocancel};
10 | }
11 |
12 | reset() {
13 | return true;
14 | }
15 | }
16 |
17 | let WrappedConfirmActions = alt.createActions(ConfirmActions)
18 | export default WrappedConfirmActions
--------------------------------------------------------------------------------
/app/api/WalletApi.js:
--------------------------------------------------------------------------------
1 | import {SerializerValidation, TransactionBuilder, TransactionHelper} from "bitsharesjs";
2 | import ApplicationApi from "./ApplicationApi";
3 |
4 | class WalletApi {
5 |
6 | constructor() {
7 | this.application_api = new ApplicationApi()
8 | }
9 |
10 | new_transaction() {
11 | return new TransactionBuilder()
12 | }
13 |
14 | sign_and_broadcast( tr, broadcast = true ) {
15 | SerializerValidation.required(tr, "transaction")
16 | return WalletDb.process_transaction(
17 | tr,
18 | null, //signer_private_key,
19 | broadcast
20 | )
21 | }
22 |
23 | /** Console print any transaction object with zero default values. */
24 | template(transaction_object_name) {
25 | var object = TransactionHelper.template(
26 | transaction_object_name,
27 | {use_default: true, annotate: true}
28 | )
29 | // visual
30 | console.error(JSON.stringify(object,null,4))
31 |
32 | // usable
33 | object = TransactionHelper.template(
34 | transaction_object_name,
35 | {use_default: true, annotate: false}
36 | )
37 | // visual
38 | console.error(JSON.stringify(object))
39 | return object
40 | }
41 |
42 | transfer(
43 | from_account_id,
44 | to_account_id,
45 | amount,
46 | asset,
47 | memo_message,
48 | broadcast = true,
49 | encrypt_memo = true,
50 | optional_nonce = null
51 | ) {
52 | console.error("deprecated, call application_api.transfer instead")
53 | return this.application_api.transfer({
54 | from_account_id,
55 | to_account_id,
56 | amount,
57 | asset,
58 | memo_message,
59 | broadcast,
60 | encrypt_memo,
61 | optional_nonce
62 | })
63 | }
64 |
65 | }
66 | export default WalletApi
67 |
--------------------------------------------------------------------------------
/app/api/accountApi.js:
--------------------------------------------------------------------------------
1 | import {ChainTypes} from "bitsharesjs";
2 | import {Apis} from "bitsharesjs-ws";
3 |
4 | let op_history = parseInt(ChainTypes.operation_history, 10);
5 |
6 | class Api {
7 |
8 | lookupAccounts(startChar, limit) {
9 | return Apis.instance().db_api().exec("lookup_accounts", [
10 | startChar, limit
11 | ]);
12 | }
13 | }
14 |
15 | export default new Api();
16 |
--------------------------------------------------------------------------------
/app/assets/fonts/WebSymbols-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/fonts/WebSymbols-Regular.otf
--------------------------------------------------------------------------------
/app/assets/imgs/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/close.png
--------------------------------------------------------------------------------
/app/assets/imgs/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/open.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/bkt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/bkt.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/blockpay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/blockpay.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/btc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/btc.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/bts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/bts.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/btsr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/btsr.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/btwty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/btwty.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/cny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/cny.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/dao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/dao.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/dash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/dash.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/dct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/dct.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/dgd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/dgd.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/eth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/eth.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/eur.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/eur.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/eurt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/eurt.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/game.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/game.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/gold.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/grc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/grc.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/hempsweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/hempsweet.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/icoo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/icoo.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/incnt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/incnt.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/lisk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/lisk.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/mkr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/mkr.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/nxc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/nxc.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/obits.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/obits.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/open.btc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/open.btc.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/peerplays.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/peerplays.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/steem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/steem.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/usd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/usd.png
--------------------------------------------------------------------------------
/app/assets/imgs/symbols/usdt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/app/assets/imgs/symbols/usdt.png
--------------------------------------------------------------------------------
/app/assets/loader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/17.
3 | */
4 | import './styles/app.scss';
5 | import './styles/symbols.scss';
6 | import "file-loader?name=dictionary.json!../../common/dictionary_en.json";
7 | import "whatwg-fetch";
--------------------------------------------------------------------------------
/app/assets/styles/define.scss:
--------------------------------------------------------------------------------
1 | //定义主色调
2 | $blueColor: #12b7f5 !default;
3 | $orangeRedColor: #ff8c00 !default;
4 | $greenColor: #228b22 !default;
5 | $whiteColor: #ffffff !default;
6 | $blackColor: #666666 !default;
7 | $blackTColor: rgba(102, 102, 102, 0.9);
8 | $whiteTColor:rgba(255, 255, 255, 0.6);
9 | $grayColor: #cccccc !default;
10 | $bgGrayColor: #dddddd !default;
11 |
12 | //定义字体大小
13 | $icoFontSize: .4rem;
14 | $titleFontSize: .3rem;
15 | $fontSize: .24rem;
16 | $middleSize: .2rem;
17 | $smallFontSize: .18rem;
18 |
19 | //定义图标字体
20 | @font-face {
21 | font-family: 'WebSymbols-Regular';
22 | src: url('../fonts/WebSymbols-Regular.otf');
23 | }
24 |
25 | $basefamily: \5FAE\8F6F\96C5\9ED1;
--------------------------------------------------------------------------------
/app/assets/styles/flip.scss:
--------------------------------------------------------------------------------
1 | @keyframes modal-flip-enter {
2 | from {
3 | transform: perspective(4rem) rotate3d(1, 0, 0, 80deg);
4 | }
5 | 70% {
6 | transform: perspective(4rem) rotate3d(1, 0, 0, -15deg);
7 | }
8 | to {
9 | transform: perspective(4rem);
10 | }
11 | }
12 | .modal-flip-enter {
13 | animation: modal-flip-enter both ease-in;
14 | backface-visibility: visible !important;
15 | }
16 | @keyframes modal-flip-leave {
17 | from {
18 | transform: perspective(4rem);
19 | }
20 | 30% {
21 | transform: perspective(4rem) rotate3d(1, 0, 0, -15deg);
22 | }
23 | to {
24 | transform: perspective(4rem) rotate3d(1, 0, 0, 80deg);
25 | }
26 | }
27 | .modal-flip-leave {
28 | animation: modal-flip-leave both;
29 | backface-visibility: visible !important;
30 | }
--------------------------------------------------------------------------------
/app/assets/styles/modal.scss:
--------------------------------------------------------------------------------
1 | .modal,
2 | .modal-mask {
3 | top: 0;
4 | left: 0;
5 | width: 100%;
6 | height: 100%;
7 | z-index: 2000;
8 | }
9 |
10 | .modal {
11 | position: fixed;
12 | font-size: $fontSize;
13 | }
14 |
15 | /* -- mask -- */
16 | .modal-mask {
17 | position: absolute;
18 | background: rgba(0, 0, 0, .3);
19 | }
20 |
21 | /* -- dialog -- */
22 | .modal-dialog {
23 | position: absolute;
24 | top: 0;
25 | left: 0;
26 | right: 0;
27 | bottom: 0;
28 | margin: auto;
29 | z-index: 2001;
30 | padding: .15rem;
31 | background: #fff;
32 | border-radius: .04rem;
33 | box-shadow: 0 .01rem .03rem rgba(0, 0, 0, .2);
34 | }
35 |
36 | /* -- close button -- */
37 | .modal-close {
38 | position: absolute;
39 | cursor: pointer;
40 | top: .1rem;
41 | right: .1rem;
42 | width: .32rem;
43 | height: .32rem;
44 | }
45 |
46 | .modal-close:before,
47 | .modal-close:after {
48 | position: absolute;
49 | content: '';
50 | height: .02rem;
51 | width: 100%;
52 | top: 50%;
53 | left: 0;
54 | margin-top: -.01px;
55 | background: #999;
56 | border-radius: 100%;
57 | transition: background .2s;
58 | }
59 |
60 | .modal-close:before {
61 | transform: rotate(45deg);
62 | }
63 |
64 | .modal-close:after {
65 | transform: rotate(-45deg);
66 | }
67 |
68 | .modal-close:hover:before,
69 | .modal-close:hover:after {
70 | background: #333;
71 | }
72 | //翻转动画
73 | @import "flip";
--------------------------------------------------------------------------------
/app/assets/styles/symbols.scss:
--------------------------------------------------------------------------------
1 | // Core asset
2 | .bts {
3 | background-image: url("../imgs/symbols/bts.png");
4 | }
5 | // BitAssets
6 | .usd {
7 | background-image: url("../imgs/symbols/usd.png");
8 | }
9 | .eur {
10 | background-image: url("../imgs/symbols/eur.png");
11 | }
12 | .cny {
13 | background-image: url("../imgs/symbols/cny.png");
14 | }
15 | .gold {
16 | background-image: url("../imgs/symbols/gold.png");
17 | }
18 | .btc {
19 | background-image: url("../imgs/symbols/btc.png");
20 | }
21 | .silver {
22 | background-image: url("../imgs/symbols/bts.png");
23 | }
24 | // 3rd party assets
25 | .eth {
26 | background-image: url("../imgs/symbols/eth.png");
27 | }
28 | .steem {
29 | background-image: url("../imgs/symbols/steem.png");
30 | }
31 | .mkr {
32 | background-image: url("../imgs/symbols/mkr.png");
33 | }
34 | .dgd {
35 | background-image: url("../imgs/symbols/dgd.png");
36 | }
37 | .obits {
38 | background-image: url("../imgs/symbols/obits.png");
39 | }
40 | .btsr {
41 | background-image: url("../imgs/symbols/btsr.png");
42 | }
43 | .dao {
44 | background-image: url("../imgs/symbols/dao.png");
45 | }
46 | .lisk {
47 | background-image: url("../imgs/symbols/lisk.png");
48 | }
49 | .peerplays {
50 | background-image: url("../imgs/symbols/peerplays.png");
51 | }
52 | .icoo {
53 | background-image: url("../imgs/symbols/icoo.png");
54 | }
55 | .blockpay {
56 | background-image: url("../imgs/symbols/blockpay.png");
57 | }
58 | .dash {
59 | background-image: url("../imgs/symbols/dash.png");
60 | }
61 | .eurt {
62 | background-image: url("../imgs/symbols/eurt.png");
63 | }
64 | .game {
65 | background-image: url("../imgs/symbols/game.png");
66 | }
67 | .grc {
68 | background-image: url("../imgs/symbols/grc.png");
69 | }
70 | .usdt {
71 | background-image: url("../imgs/symbols/usdt.png");
72 | }
73 | .bkt {
74 | background-image: url("../imgs/symbols/bkt.png");
75 | }
76 | .dct {
77 | background-image: url("../imgs/symbols/dct.png");
78 | }
79 | .incnt {
80 | background-image: url("../imgs/symbols/incnt.png");
81 | }
82 | .nxc {
83 | background-image: url("../imgs/symbols/nxc.png");
84 | }
85 | .btwty {
86 | background-image: url("../imgs/symbols/btwty.png");
87 | }
88 | .open-btc {
89 | background-image: url("../imgs/symbols/open.btc.png");
90 | }
91 | .hempsweet {
92 | background-image: url("../imgs/symbols/hempsweet.png");
93 | }
--------------------------------------------------------------------------------
/app/components/BaseComponent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/14.
3 | */
4 | import React from "react";
5 | import {intlShape} from 'react-intl';
6 |
7 | /**
8 | * 核心资产ID
9 | * @type {string}
10 | */
11 | export const CORE_ASSET_ID = "1.3.0";
12 |
13 | /**
14 | * 组件基类
15 | * 简单封装了intl实例和router对象
16 | */
17 | class BaseComponent extends React.Component {
18 | static contextTypes = {
19 | intl: intlShape.isRequired,
20 | router: React.PropTypes.object
21 | };
22 |
23 | constructor(props) {
24 | super(props);
25 | this.formatMessage = this.formatMessage.bind(this);
26 | }
27 |
28 | formatMessage(id) {
29 | //console.debug('arguments.length',arguments.length);
30 | let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
31 | //console.debug(values)
32 | return this.context.intl.formatMessage({id: id}, values);
33 | }
34 | }
35 |
36 | export default BaseComponent;
--------------------------------------------------------------------------------
/app/components/Blockchain/BlockTime.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import BindToChainState from "../Utility/BindToChainState";
3 | import ChainTypes from "../Utility/ChainTypes";
4 | import TimeAgo from "../Utility/TimeAgo";
5 | import utils from "../../../common/utils";
6 |
7 | class BlockTime extends React.Component {
8 |
9 | static propTypes = {
10 | block_number: React.PropTypes.number.isRequired,
11 | globalObject: ChainTypes.ChainObject.isRequired,
12 | dynGlobalObject: ChainTypes.ChainObject.isRequired
13 | };
14 |
15 | static defaultProps = {
16 | globalObject: "2.0.0",
17 | dynGlobalObject: "2.1.0"
18 | };
19 |
20 | constructor(props) {
21 | super(props);
22 | this.state = {time: null};
23 | }
24 |
25 | componentDidMount() {
26 | this.calcTime(this.props.block_number);
27 | }
28 |
29 | calcTime(block_number) {
30 | this.setState({time: utils.calc_block_time(block_number, this.props.globalObject, this.props.dynGlobalObject)});
31 | }
32 |
33 | componentWillReceiveProps(next_props) {
34 | if(next_props.block_number !== this.props.block_number) {
35 | this.calcTime(next_props.block_number);
36 | }
37 | }
38 |
39 | /*
40 | shouldComponentUpdate(next_props, next_state) {
41 | return next_props.block_number !== this.props.block_number || next_state.time !== this.state.time;
42 | }
43 | */
44 |
45 | //{this.state.time ? : null}
46 | render() {
47 | return (
48 |
49 | {this.state.time ? : null }
50 |
51 | );
52 | }
53 | }
54 | BlockTime = BindToChainState(BlockTime, {keep_updating: true});
55 |
56 | export default BlockTime;
57 |
--------------------------------------------------------------------------------
/app/components/Blockchain/MemoInfo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/2/16.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import utils from "../../../common/utils";
7 | import connectToStores from 'alt-utils/lib/connectToStores';
8 |
9 | //stores
10 | import PrivateKeyStore from "../../stores/PrivateKeyStore";
11 | import WalletUnlockStore from "../../stores/WalletUnlockStore";
12 |
13 | //actions
14 | import WalletUnlockActions from "../../actions/WalletUnlockActions";
15 |
16 | class MemoInfo extends BaseComponent {
17 | static defaultProps = {
18 | fullLength: false
19 | };
20 |
21 | constructor(props) {
22 | super(props);
23 | }
24 |
25 | shouldComponentUpdate(nextProps) {
26 | return (
27 | !utils.are_equal_shallow(nextProps.memo, this.props.memo) ||
28 | nextProps.wallet_locked !== this.props.wallet_locked
29 | );
30 | }
31 |
32 | unLock(e) {
33 | e.preventDefault();
34 | WalletUnlockActions.unlock().then(() => {
35 | this.forceUpdate();
36 | })
37 | }
38 |
39 | render() {
40 | let {memo, fullLength} = this.props;
41 | if (!memo) {
42 | return null;
43 | }
44 |
45 | let {text, isMine} = PrivateKeyStore.decodeMemo(memo);
46 |
47 | if (!text && isMine) {
48 | return (
49 |
50 | {this.formatMessage('transfer_memoUnlock')}
51 |
52 | );
53 | }
54 | let full_memo = text;
55 | if (text && !fullLength && text.length > 35) {
56 | text = text.substr(0, 35) + "...";
57 | }
58 |
59 | if (text) {
60 | return (
61 |
62 |
63 | {text}
64 |
65 |
66 | );
67 | } else {
68 | return null;
69 | }
70 | }
71 | }
72 |
73 | class MemoInfoWrapper extends React.Component {
74 | static getPropsFromStores() {
75 | return {wallet_locked: WalletUnlockStore.getState().locked};
76 | }
77 |
78 | static getStores() {
79 | return [WalletUnlockStore];
80 | }
81 |
82 | render() {
83 | return ;
84 | }
85 | }
86 | export default connectToStores(MemoInfoWrapper);
--------------------------------------------------------------------------------
/app/components/LastOperation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/17.
3 | */
4 | import React from "react";
5 | import BaseComponent from "./BaseComponent";
6 | import {ChainTypes as grapheneChainTypes} from "bitsharesjs";
7 | import AltContainer from "alt-container";
8 | import {RecentTransactions} from "./dashboard/Operation";
9 |
10 | //stores
11 | import AccountStore from "../stores/AccountStore";
12 |
13 |
14 | const {operations} = grapheneChainTypes;
15 |
16 |
17 |
18 | class LastOperation extends BaseComponent {
19 | constructor(props) {
20 | super(props);
21 | }
22 | getHistory(accountsList, filterOp, customFilter) {
23 | let history = [];
24 | let seen_ops = new Set();
25 | for (let account of accountsList) {
26 | if(account) {
27 | let h = account.get("history");
28 | if (h) history = history.concat(h.toJS().filter(op => !seen_ops.has(op.id) && seen_ops.add(op.id)));
29 | }
30 | }
31 | if (filterOp) {
32 | history = history.filter(a => {
33 | return a.op[0] === operations[filterOp];
34 | });
35 | }
36 |
37 | if (customFilter) {
38 | history = history.filter(a => {
39 | let finalValue = customFilter.fields.reduce((final, filter) => {
40 | switch (filter) {
41 | case "asset_id":
42 | return final && a.op[1]["amount"][filter] === customFilter.values[filter];
43 | break;
44 | default:
45 | return final && a.op[1][filter] === customFilter.values[filter];
46 | break;
47 | }
48 | }, true)
49 | return finalValue;
50 | });
51 | }
52 | return history;
53 | }
54 |
55 | render() {
56 | let {linkedAccounts} = this.props;
57 | let accountCount = linkedAccounts.size;
58 |
59 | return (
60 |
61 |
62 |
63 | {this.formatMessage("lastOperation_operation")}
64 | {this.formatMessage("lastOperation_info")}
65 |
66 |
67 | {accountCount ?
:
71 |
72 |
73 | }
74 |
75 |
76 | );
77 | }
78 | }
79 |
80 | class LastOperationContainer extends React.Component {
81 | render() {
82 | return (
83 | {
87 | return AccountStore.getState().linkedAccounts;
88 | }
89 | }}>
90 |
91 |
92 | );
93 | }
94 | }
95 | export default LastOperationContainer;
--------------------------------------------------------------------------------
/app/components/Loading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/21.
3 | */
4 | import React from 'react';
5 |
6 |
7 | class Loading extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {progress: 0};
11 | }
12 |
13 | componentDidMount() {
14 | this.timer = setInterval(() => {
15 | let p = this.state.progress;
16 | p += 1;
17 | if (p > 7) p = 0;
18 | this.setState({progress: p});
19 | }, 70);
20 | }
21 |
22 | componentWillUnmount() {
23 | this.timer && clearTimeout(this.timer);
24 | this.timer = null;
25 | }
26 |
27 | render() {
28 | return (
29 |
30 |
{this.state.progress}
31 |
32 | );
33 | }
34 | }
35 |
36 | export default Loading;
--------------------------------------------------------------------------------
/app/components/NavigationBar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/10.
3 | */
4 | import React from 'react';
5 | import BaseComponent from "./BaseComponent";
6 | import AltContainer from "alt-container";
7 | import WalletUnlockStore from "../stores/WalletUnlockStore";
8 | import WalletDb from "../stores/WalletDb";
9 |
10 | //actions
11 | import WalletUnlockActions from "../actions/WalletUnlockActions";
12 | import PopupMenu, {menuItems} from './PopupMenu';
13 | import ScanActions from "../actions/ScanActions";
14 |
15 | class NavigationBar extends BaseComponent {
16 |
17 | constructor(props) {
18 | super(props);
19 | this.state = {
20 | menuTop: '0',
21 | menuLeft: '0',
22 | isShowMenu: false
23 | };
24 | }
25 |
26 | showMenu() {
27 | let rect = this.refs.menuBtn.getBoundingClientRect();
28 | /*
29 | let menu = this.refs.mainMenu;
30 | menu.setState({top: rect.top + rect.height - 30, left: rect.left - 145, isShow: !menu.state.isShow});
31 | */
32 | //console.debug(rect);
33 | this.setState({
34 | menuTop: '.6rem',//(rect.top + rect.height) / 100,
35 | menuLeft: '.12rem'// (rect.left + rect.width) / 100,
36 | });
37 | let menuState = this.refs.menu.state;
38 | this.refs.menu.setState({isShow: !menuState.isShow});
39 | }
40 |
41 | onMenuClick(data) {
42 | console.debug(data);
43 | //this.setState({isShow: false});
44 | //browserHistory.push(data.url);
45 | }
46 |
47 | onMenuItemClick(data) {
48 | //console.debug(data);
49 | if (data.url === 'reload') {
50 | window.location.reload();
51 | } else if (data.url === 'scan') {
52 | ScanActions.scan(this.context.router.location);
53 | this.context.router.push('/' + data.url);
54 | }
55 | }
56 |
57 | onBackClick() {
58 | this.context.router.goBack();
59 | }
60 |
61 | onUnlockClick(e) {
62 | e.preventDefault();
63 | let wallet = WalletDb.getWallet();
64 | if (!wallet) {
65 | this.context.router.push('/create-account');
66 | return;
67 | }
68 | if (WalletDb.isLocked()) WalletUnlockActions.unlock();
69 | else WalletUnlockActions.lock();
70 | }
71 |
72 | getTitle() {
73 | let url = this.context.router.location.pathname;
74 | if (url === "/") {
75 | return this.context.intl.formatMessage({id: "menu_index"});
76 | }
77 | if(url==="/init-error"){
78 | return this.context.intl.formatMessage({id:"menu_settings"});
79 | }
80 | url = url.substring(1);
81 | let menu = menuItems.find((item) => {
82 | if (item.url === "/") return false;
83 | return url.startsWith(item.url.substring(1));
84 | });
85 | if (menu !== undefined) {
86 | return this.context.intl.formatMessage({id: menu.name});
87 | } else {
88 | return null;
89 | }
90 | }
91 |
92 | //SettingsStore.getSetting('apiServer')
93 | //()=>SettingsActions.changeSetting({setting: "locale", value: "cn"})
94 |
95 | render() {
96 | let titleClass = "top-title";
97 | if (this.context.router.location.pathname == "/init-error") {
98 | return (
99 |
100 |
{this.getTitle()}
101 |
102 | );
103 | }
104 | let props = this.props;
105 | //console.debug(this.context.router);
106 | let backBtn = null;
107 | if (this.context.router.location.pathname !== "/") {
108 | backBtn = (<
);
109 | } else {
110 | titleClass = "top-left-title";
111 | }
112 | return (
113 |
114 |
{this.getTitle()}
115 | {backBtn}
116 |
117 |
118 | {this.props.locked ? 'x' : 'w'}
119 |
120 |
p
121 |
122 |
125 |
126 | );
127 | }
128 | }
129 |
130 | class NavigationBarContainer extends React.Component {
131 | render() {
132 | return (
133 |
134 |
135 |
136 | )
137 | }
138 | }
139 |
140 | export default NavigationBarContainer
--------------------------------------------------------------------------------
/app/components/PopupMenu.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/20.
3 | */
4 | import React from 'react';
5 | import {injectIntl, intlShape, FormattedMessage} from 'react-intl';
6 |
7 | export const menuItems = [
8 | {name: 'menu_index', url: '/'},
9 | {name: 'menu_transaction', url: '/markets'},//transaction
10 | {name: 'menu_transfer', url: '/transfer'},
11 | {name: 'menu_lastOperate', url: '/last-operate'},
12 | {name: 'menu_scan', url: 'scan'},
13 | {name: 'menu_createAccount', url: '/create-account'},
14 | {name: 'menu_settings', url: '/settings'},
15 | {name: "menu_reload", url: 'reload'}
16 | ];
17 |
18 | class PopupMenu extends React.Component {
19 | constructor(props) {
20 | super(props);
21 | this.state = {isShow: false};
22 | this.onDocumentClick = this.onDocumentClick.bind(this);
23 | }
24 |
25 | componentDidUpdate(prevProps, prevState) {
26 | if (this.state.isShow) {
27 | document.addEventListener('click', this.onDocumentClick, false);
28 | } else {
29 | document.removeEventListener('click', this.onDocumentClick, false);
30 | }
31 | }
32 |
33 | onMenuClick(data, e) {
34 | e.nativeEvent.stopImmediatePropagation();
35 | this.setState({isShow: false});
36 | //browserHistory.push(data.url);
37 | if (data.url.startsWith('/')) {
38 | this.context.router.push(data.url);
39 | }
40 | else {
41 | this.props.onMenuItemClick && this.props.onMenuItemClick(data);
42 | }
43 | }
44 |
45 | onDocumentClick(e) {
46 | this.setState({isShow: false});
47 | }
48 |
49 | render() {
50 | let props = this.props;
51 | //let omc = props.onItemClick || this.onMenuClick.bind(this);
52 | if (this.state.isShow) {
53 | return (
54 |
66 | );
67 | } else {
68 | return null;
69 | }
70 | }
71 | }
72 | PopupMenu.propTypes = {
73 | top: React.PropTypes.string,
74 | left: React.PropTypes.string,
75 | onMenuItemClick: React.PropTypes.func
76 | };
77 | PopupMenu.contextTypes = {
78 | intl: intlShape.isRequired,
79 | router: React.PropTypes.object
80 | };
81 | PopupMenu.defaultProps = {
82 | top: 0,
83 | left: 0,
84 | onMenuItemClick: null
85 | };
86 |
87 |
88 | export default PopupMenu;
--------------------------------------------------------------------------------
/app/components/Root.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | //stores
4 | import AccountStore from "../stores/AccountStore";
5 | import SettingsStore from '../stores/SettingsStore';
6 | import NotificationStore from "../stores/NotificationStore";
7 |
8 | import {ChainStore} from "bitsharesjs";
9 | import {Apis} from "bitsharesjs-ws";
10 |
11 | //组件
12 | import Loading from './Loading';
13 | import NavigationBar from './NavigationBar';
14 | import NotificationSystem from "react-notification-system";
15 | import TransactionConfirm from "./TransactionConfirm";
16 | import UnlockWallet from "./wallet/UnlockWallet";
17 | import Settings from './Settings';
18 | import Confirm from './layout/Confirm';
19 | import GlobalSettingContainer from './containers/GlobalSettingContainer';
20 |
21 | class Root extends React.Component {
22 | constructor(props) {
23 | super(props);
24 | this.state = {
25 | loading: true,
26 | synced: false,
27 | syncFail: false,
28 | disableChat: SettingsStore.getState().settings.get("disableChat", true)
29 | };
30 | }
31 |
32 | onNotificationChange() {
33 | let notification = NotificationStore.getState().notification;
34 | if (notification.autoDismiss === void 0) {
35 | notification.autoDismiss = 10;
36 | }
37 | if (this.refs.notificationSystem) this.refs.notificationSystem.addNotification(notification);
38 | }
39 |
40 | componentWillUnmount() {
41 | NotificationStore.unlisten(this.onNotificationChange);
42 | SettingsStore.unlisten(this.onSettingsChange);
43 | }
44 |
45 | componentDidMount() {
46 | try {
47 | NotificationStore.listen(this.onNotificationChange.bind(this));
48 | SettingsStore.listen(this.onSettingsChange.bind(this));
49 | ChainStore.init().then(() => {
50 | this.setState({synced: true});
51 | Promise.all([
52 | AccountStore.loadDbData()
53 | ]).then(() => {
54 | AccountStore.tryToSetCurrentAccount();
55 | this.setState({loading: false, syncFail: false});
56 | }).catch(error => {
57 | console.log("[Root.js] ----- ERROR ----->", error);
58 | this.setState({loading: false});
59 | });
60 | }).catch(error => {
61 | console.log("[App.jsx] ----- ChainStore.init error ----->", error);
62 | let syncFail = error.message === "ChainStore sync error, please check your system clock" ? true : false;
63 | this.setState({loading: false, syncFail});
64 | });
65 |
66 | } catch (e) {
67 | console.error("error:", e);
68 | }
69 | }
70 |
71 | onSettingsChange() {
72 | let {settings} = SettingsStore.getState();
73 | if (settings.get("disableChat") !== this.state.disableChat) {
74 | this.setState({
75 | disableChat: settings.get("disableChat")
76 | });
77 | }
78 | }
79 |
80 | render() {
81 | let {disableChat, syncFail, loading} = this.state;
82 | let content = null;
83 | if (syncFail) {
84 | content = (
85 |
86 |
87 |
88 | );
89 | } else if (loading) {
90 | content = ;
91 | } else {
92 | content = (
93 |
94 |
95 | {this.props.children}
96 |
97 |
98 |
99 |
100 |
101 | );
102 | }
103 | //console.debug(content);
104 | return content;
105 | }
106 | }
107 |
108 | export default Root;
--------------------------------------------------------------------------------
/app/components/RootIntl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/27.
3 | */
4 |
5 | import React from 'react';
6 | import connectToStores from 'alt-utils/lib/connectToStores';
7 | import IntlStore from '../stores/IntlStore';
8 | import IntlActions from '../actions/IntlActions';
9 | import {IntlProvider} from 'react-intl';
10 | import Root from './Root';
11 | import intlData from "./Utility/intlData";
12 |
13 | class RootIntl extends React.Component {
14 | static getStores() {
15 | return [IntlStore];
16 | };
17 |
18 | static getPropsFromStores() {
19 | let state = IntlStore.getState();
20 | return {
21 | locale: state.currentLocale,
22 | localeObj: state.localesObject[state.currentLocale]
23 | };
24 | };
25 |
26 | componentDidMount() {
27 | IntlActions.switchLocale(this.props.locale);
28 | }
29 |
30 | render() {
31 | //console.debug('this.props.localeObj',this.props.localeObj);
32 | return (
33 |
38 |
39 |
40 | );
41 | }
42 | }
43 |
44 | export default connectToStores(RootIntl);
--------------------------------------------------------------------------------
/app/components/Settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/21.
3 | */
4 |
5 | import React from 'react';
6 |
7 | class Settings extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | }
11 | render() {
12 | return (
13 |
14 | {this.props.children}
15 |
16 | );
17 | }
18 | }
19 |
20 | export default Settings;
--------------------------------------------------------------------------------
/app/components/TextLoading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/2/3.
3 | */
4 | import React from 'react';
5 |
6 |
7 | class TextLoading extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {progress: 0};
11 | }
12 |
13 | componentDidMount() {
14 | this.timer = setInterval(() => {
15 | let p = this.state.progress;
16 | p += 1;
17 | if (p > 7) p = 0;
18 | this.setState({progress: p});
19 | }, 70);
20 | }
21 |
22 | componentWillUnmount() {
23 | this.timer && clearTimeout(this.timer);
24 | this.timer = null;
25 | }
26 |
27 | render() {
28 | return (
29 | {this.state.progress}
30 | );
31 | }
32 | }
33 |
34 | export default TextLoading;
--------------------------------------------------------------------------------
/app/components/TransactionConfirm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/1/29.
3 | */
4 | import React from "react";
5 | import BaseComponent from "./BaseComponent";
6 | import Modal from "./layout/Modal";
7 | import Transaction from "./Blockchain/Transaction";
8 | import TextLoading from "./TextLoading";
9 | import AltContainer from "alt-container";
10 |
11 | import {ChainStore} from "bitsharesjs";
12 |
13 | //actions
14 | import TransactionConfirmActions from "../actions/TransactionConfirmActions";
15 |
16 | //ChainStores
17 | import WalletDb from "../stores/WalletDb";
18 | import TransactionConfirmStore from "../stores/TransactionConfirmStore";
19 |
20 | class TransactionConfirm extends BaseComponent {
21 | constructor(props) {
22 | super(props);
23 | this.state = {visible: false};
24 | this.show = this.show.bind(this);
25 | this.hide = this.hide.bind(this);
26 | }
27 |
28 | componentWillReceiveProps(nextProps) {
29 | if (nextProps.closed !== this.props.closed) {
30 | if (!nextProps.closed) {
31 | this.show();
32 | } else {
33 | this.hide(true);
34 | }
35 | }
36 | }
37 |
38 | show() {
39 | this.setState({visible: true});
40 | }
41 |
42 | hide(ok) {
43 | this.setState({visible: false});
44 | }
45 |
46 | onConfirmClick(e) {
47 | e.preventDefault();
48 | if (this.props.propose) {
49 | TransactionConfirmActions.close();
50 | const propose_options = {
51 | fee_paying_account: ChainStore.getAccount(this.props.fee_paying_account).get("id")
52 | };
53 | this.props.transaction.update_head_block().then(() => {
54 | WalletDb.process_transaction(this.props.transaction.propose(propose_options), null, true);
55 | });
56 | } else
57 | TransactionConfirmActions.broadcast(this.props.transaction);
58 | }
59 |
60 | onCloseClick(e) {
61 | e.preventDefault && e.preventDefault();
62 | TransactionConfirmActions.close();
63 | }
64 |
65 | render() {
66 | let {broadcast, broadcasting} = this.props;
67 | if (!this.props.transaction || this.props.closed) {
68 | return null;
69 | }
70 |
71 | let title = null;
72 | let button = (
73 |
74 |
76 |
77 | );
78 | if (this.props.error || this.props.included) {
79 | if (this.props.error) {
80 | title = (
81 |
82 | {this.formatMessage('transaction_broadcast_fail')}
83 | {this.props.error}
84 |
85 | );//transaction_broadcast_fail transaction_confirm
86 | } else {
87 | title = (
88 |
89 | {this.formatMessage('transaction_confirm')}
90 | #{this.props.trx_id}@{this.props.trx_block_num}
91 |
92 | );
93 | }
94 | } else if (broadcast) {
95 | title = (
96 |
97 | {this.formatMessage('transaction_broadcast_success')}
98 | {this.formatMessage('transaction_waiting')}
99 |
100 | );
101 | } else if (broadcasting) {
102 | title = (
103 |
104 | {this.formatMessage('transaction_broadcasting')}
105 |
106 |
107 | );
108 | button = null;
109 | } else {
110 | title = (
111 |
112 | {this.formatMessage('transaction_confirm')}
113 |
114 | );
115 | button = (
116 |
117 |
119 |
120 |
122 |
123 | );
124 | }
125 | return (
126 |
127 |
130 | {title}
131 |
132 | {button}
133 |
134 |
135 | );
136 | }
137 | }
138 |
139 | class TransactionConfirmContainer extends React.Component {
140 | render() {
141 | return (
142 |
143 |
144 |
145 | )
146 | }
147 | }
148 | export default TransactionConfirmContainer
--------------------------------------------------------------------------------
/app/components/Utility/AccountImage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/2/19.
3 | */
4 | import React,{PropTypes} from "react";
5 | import Identicon from "./Identicon";
6 |
7 | class AccountImage extends React.Component{
8 | static propTypes = {
9 | src: PropTypes.string,
10 | account: PropTypes.string,
11 | size: PropTypes.object.isRequired
12 | };
13 | static defaultProps = {
14 | src: "",
15 | account: "",
16 | size: {height: 120, width: 120}
17 | };
18 |
19 | render(){
20 | let {account, image} = this.props;
21 | let {height, width} = this.props.size;
22 | let custom_image = image ?
23 |
:(account?
24 | :);
25 | return (
26 | U{custom_image}
27 | );
28 | }
29 | }
30 |
31 | export default AccountImage;
--------------------------------------------------------------------------------
/app/components/Utility/AccountName.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/2/17.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent"
6 | import BindToChainState from "../Utility/BindToChainState";
7 | import ChainTypes from "../Utility/ChainTypes";
8 |
9 | class AccountName extends BaseComponent {
10 | static propTypes = {
11 | account: ChainTypes.ChainObject.isRequired
12 | };
13 |
14 | constructor(props) {
15 | super(props);
16 | }
17 |
18 | shouldComponentUpdate(nextProps) {
19 | if (nextProps.account.get("name") && this.props.account.get("name")
20 | && nextProps.account.get("name") === this.props.account.get("name")) {
21 | return false;
22 | }
23 | return true;
24 | }
25 |
26 | render() {
27 | let account_name = this.props.account.get("name");
28 | if (!account_name) {
29 | return {this.props.account.get("id")};
30 | }
31 | return {account_name};
32 | }
33 | }
34 | export default BindToChainState(AccountName);
--------------------------------------------------------------------------------
/app/components/Utility/AssetName.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/2/4.
3 | */
4 | import React from "react";
5 | import BindToChainState from "./BindToChainState";
6 | import ChainTypes from "./ChainTypes";
7 | import utils from "../../../common/utils";
8 |
9 | class AssetName extends React.Component {
10 | static propTypes = {
11 | asset: ChainTypes.ChainAsset.isRequired,
12 | replace: React.PropTypes.bool.isRequired,
13 | name: React.PropTypes.string.isRequired
14 | };
15 |
16 | static defaultProps = {
17 | replace: true,
18 | noPrefix: false
19 | };
20 |
21 | shouldComponentUpdate(nextProps) {
22 | return (
23 | nextProps.replace !== this.props.replace ||
24 | nextProps.name !== this.props.replace
25 | );
26 | }
27 |
28 | render() {
29 | let {name, replace, asset, noPrefix} = this.props;
30 | let isBitAsset = asset.has("bitasset");
31 | let isPredMarket = isBitAsset && asset.getIn(["bitasset", "is_prediction_market"]);
32 |
33 | let {name: replacedName, prefix} = utils.replaceName(name, isBitAsset && !isPredMarket && asset.get("issuer") === "1.2.0");
34 |
35 | if (replace && replacedName !== this.props.name) {
36 | return {!noPrefix ? prefix : null}{replacedName};
37 | } else {
38 | return {!noPrefix ? prefix : null}{name};
39 | }
40 | }
41 | }
42 |
43 | AssetName = BindToChainState(AssetName);
44 |
45 | export default class AssetNameWrapper extends React.Component {
46 |
47 | render() {
48 | return (
49 |
50 | );
51 | }
52 | }
--------------------------------------------------------------------------------
/app/components/Utility/BalanceComponent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/2/8.
3 | */
4 | import React,{PropTypes} from "react";
5 | import FormattedAsset from "./FormattedAsset";
6 | import ChainTypes from "./ChainTypes";
7 | import BindToChainState from "./BindToChainState";
8 |
9 | /**
10 | * 显示一个余额对象
11 | * balance属性为余额对象的id
12 | */
13 | class BalanceComponent extends React.Component {
14 |
15 | static propTypes = {
16 | balance: ChainTypes.ChainObject.isRequired,
17 | assetInfo: React.PropTypes.node,
18 | hide_asset: PropTypes.bool, //隐藏资产名
19 | hide_amount: PropTypes.bool //隐藏资产数量
20 | }
21 | static defaultProps = {
22 | hide_asset: false,
23 | hide_amount: false
24 | }
25 |
26 | getBalance(){
27 | return Number(this.props.balance.get("balance"));
28 | }
29 |
30 | render() {
31 | let amount = this.getBalance();
32 | let type = this.props.balance.get("asset_type");
33 | return ();
35 | }
36 | }
37 |
38 | export default BindToChainState(BalanceComponent, {keep_updating: true});
--------------------------------------------------------------------------------
/app/components/Utility/FormattedAsset.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/2/3.
3 | */
4 | import React, {PropTypes} from "react";
5 | import {FormattedNumber} from "react-intl";
6 | import ChainTypes from "./ChainTypes";
7 | import utils from "../../../common/utils";
8 | import BindToChainState from "./BindToChainState";
9 | import AssetName from "./AssetName";
10 |
11 |
12 | class FormattedAsset extends React.Component {
13 | static propTypes = {
14 | amount: PropTypes.any.isRequired, //数量
15 | exact_amount: PropTypes.bool, //准确数量
16 | decimalOffset: PropTypes.number, //十进制偏移
17 | asset: ChainTypes.ChainAsset.isRequired, //资产
18 | hide_asset: PropTypes.bool, //隐藏资产名
19 | hide_amount: PropTypes.bool, //隐藏资产数量
20 | asPercentage: PropTypes.bool, //作为百分比显示
21 | assetInfo: PropTypes.node, //资产信息
22 | className: PropTypes.string //样式名
23 | }
24 | static defaultProps = {
25 | amount: 0,
26 | decimalOffset: 0,
27 | hide_asset: false,
28 | hide_amount: false,
29 | asPercentage: false,
30 | assetInfo: null,
31 | className: ""
32 | }
33 |
34 | render() {
35 | let {amount, decimalOffset, asset, hide_asset, hide_amount, asPercentage} = this.props;
36 |
37 | if (asset && asset.toJS) asset = asset.toJS();
38 | let decimals = Math.max(0, asset.precision - decimalOffset);
39 | let precision = utils.get_asset_precision(asset.precision);
40 | if (asPercentage) {
41 | let supply = parseInt(asset.dynamic.current_supply, 10);
42 | let percent = utils.format_number((amount / supply) * 100, 4);
43 | return (
44 |
45 | {percent}%
46 |
47 | );
48 | }
49 | return (
50 |
51 | {hide_amount ? null :
52 |
57 | }
58 |
59 | {hide_asset ? null :
60 |
61 | }
62 |
63 | );
64 | }
65 | }
66 |
67 | FormattedAsset = BindToChainState(FormattedAsset);
68 |
69 | export default FormattedAsset;
--------------------------------------------------------------------------------
/app/components/Utility/FormattedPrice.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/2/4.
3 | */
4 | import React from "react";
5 | import {FormattedNumber} from "react-intl";
6 | import utils from "../../../common/utils";
7 | import ChainTypes from "./ChainTypes";
8 | import BindToChainState from "./BindToChainState";
9 | import AltContainer from "alt-container";
10 | import SettingsStore from "../../stores/SettingsStore";
11 | import SettingsActions from "../../actions/SettingsActions";
12 | import AssetName from "./AssetName";
13 |
14 | class FormattedPrice extends React.Component {
15 |
16 | static propTypes = {
17 | base_asset: ChainTypes.ChainAsset.isRequired,
18 | quote_asset: ChainTypes.ChainAsset.isRequired,
19 | base_amount: React.PropTypes.any,
20 | quote_amount: React.PropTypes.any,
21 | decimals: React.PropTypes.number
22 | };
23 |
24 | static contextTypes = {
25 | router: React.PropTypes.object
26 | };
27 |
28 | constructor(props) {
29 | super(props);
30 | }
31 |
32 | onFlip() {
33 | let setting = {};
34 | setting[this.props.marketId] = !this.props.marketDirections.get(this.props.marketId);
35 | SettingsActions.changeMarketDirection(setting);
36 | }
37 |
38 | shouldComponentUpdate(nextProps, nextState) {
39 | return (
40 | nextProps.marketDirections !== this.props.marketDirections ||
41 | nextProps.base_amount !== this.props.base_amount ||
42 | nextProps.quote_amount !== this.props.quote_amount ||
43 | nextProps.decimals !== this.props.decimals ||
44 | !utils.are_equal_shallow(nextState, this.state)
45 | );
46 | }
47 |
48 | goToMarket(e) {
49 | e.preventDefault();
50 | this.context.router.push(`/market/${this.props.base_asset.get("symbol")}_${this.props.quote_asset.get("symbol")}`);
51 | }
52 |
53 | render() {
54 |
55 | let {base_asset, quote_asset, base_amount, quote_amount, callPrice,
56 | marketDirections, marketId, hide_symbols} = this.props;
57 |
58 | let invertPrice = marketDirections.get(marketId);
59 | if( (invertPrice && !callPrice) || this.props.invert) {
60 | let tmp = base_asset;
61 | base_asset = quote_asset;
62 | quote_asset = tmp;
63 | let tmp_amount = base_amount;
64 | base_amount = quote_amount;
65 | quote_amount = tmp_amount;
66 | }
67 |
68 | let formatted_value = "";
69 | if (!this.props.hide_value) {
70 | let base_precision = utils.get_asset_precision(base_asset.get("precision"));
71 | let quote_precision = utils.get_asset_precision(quote_asset.get("precision"));
72 | let value = base_amount / base_precision / (quote_amount / quote_precision);
73 | if (isNaN(value) || !isFinite(value)) {
74 | return n/a;
75 | }
76 | let decimals = this.props.decimals ? this.props.decimals : base_asset.get("precision") + quote_asset.get("precision");
77 | decimals = Math.min(8, decimals);
78 | if (base_asset.get("id") === "1.3.0") {
79 | base_asset.get("precision");
80 | }
81 | formatted_value = (
82 |
87 | );
88 | }
89 | let symbols = hide_symbols ? "" :
90 | (/);
91 |
92 | return (
93 | {formatted_value} {symbols}
94 | );
95 | }
96 | }
97 |
98 | FormattedPrice = BindToChainState(FormattedPrice);
99 |
100 | export default class FormattedPriceWrapper extends React.Component {
101 |
102 | render() {
103 | let marketId = this.props.quote_asset + "_" + this.props.base_asset;
104 |
105 | return (
106 | {
110 | return SettingsStore.getState().marketDirections;
111 | }
112 | }}
113 | >
114 |
115 |
116 | );
117 | }
118 | }
--------------------------------------------------------------------------------
/app/components/Utility/Identicon.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {PropTypes, Component} from "react";
3 | import sha256 from "js-sha256";
4 | import jdenticon from "jdenticon";
5 |
6 | var canvas_id_count = 0;
7 |
8 | class Identicon extends Component {
9 |
10 | constructor(props) {
11 | super(props);
12 | this.canvas_id = "identicon_" + (this.props.account||"") + (++canvas_id_count);
13 | }
14 |
15 | shouldComponentUpdate(nextProps) {
16 | return nextProps.size.height !== this.props.size.height || nextProps.size.width !== this.props.size.width || nextProps.account !== this.props.account;
17 | }
18 |
19 | render() {
20 | let {account} = this.props;
21 | let {height, width} = this.props.size;
22 | let hash = account ? sha256(account) : null;
23 | return (
24 |
25 | );
26 | }
27 |
28 | repaint() {
29 | if(this.props.account) jdenticon.updateById(this.canvas_id);
30 | else {
31 | let ctx = this.refs.canvas.getContext('2d');
32 | ctx.fillStyle = "rgba(255, 255, 255, 0.2)";
33 | let size = ctx.canvas.width;
34 | ctx.clearRect(0, 0, size, size);
35 | ctx.fillRect(0, 0, size, size);
36 | ctx.clearRect(0+1, 0+1, size-2, size-2);
37 | ctx.font = `${size}px sans-serif`;
38 | ctx.fillText("?", size/4, size - size/6);
39 | }
40 | }
41 |
42 | componentDidMount() {
43 | this.repaint();
44 | }
45 |
46 | componentDidUpdate() {
47 | this.repaint();
48 | }
49 | }
50 |
51 | Identicon.propTypes = {
52 | size: PropTypes.object.isRequired,
53 | account: PropTypes.string
54 | };
55 |
56 | export default Identicon;
--------------------------------------------------------------------------------
/app/components/Utility/PriceText.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import utils from "../../../common/utils";
3 |
4 | class PriceText extends React.Component {
5 |
6 | render() {
7 | let {price, preFormattedPrice, quote, base, component} = this.props;
8 |
9 | let formattedPrice = preFormattedPrice ? preFormattedPrice : utils.price_to_text(price, quote, base);
10 |
11 | if (formattedPrice.full >= 1) {
12 | return (
13 |
14 | {formattedPrice.int}.
15 | {formattedPrice.dec ? {formattedPrice.dec} : null}
16 | {formattedPrice.trailing ? {formattedPrice.trailing} : null}
17 |
18 | )
19 | } else if (formattedPrice.full >= 0.1) {
20 | return (
21 |
22 | {formattedPrice.int}.
23 | {formattedPrice.dec ? {formattedPrice.dec} : null}
24 | {formattedPrice.trailing ? {formattedPrice.trailing} : null}
25 |
26 | )
27 | } else {
28 | return (
29 |
30 | {formattedPrice.int}.
31 | {formattedPrice.dec ? {formattedPrice.dec} : null}
32 | {formattedPrice.trailing ? {formattedPrice.trailing} : null}
33 |
34 | )
35 | }
36 | }
37 | }
38 |
39 | export default PriceText;
40 |
--------------------------------------------------------------------------------
/app/components/Utility/TimeAgo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {FormattedRelative} from "react-intl";
3 | import {ChainStore} from "bitsharesjs";
4 |
5 | class TimeAgo extends React.Component {
6 |
7 | static propTypes = {
8 | time: React.PropTypes.any.isRequired,
9 | chain_time: React.PropTypes.bool,
10 | component: React.PropTypes.element,
11 | className: React.PropTypes.string
12 | };
13 |
14 | static defaultProps = {
15 | chain_time: true
16 | };
17 |
18 | shouldComponentUpdate(nextProps) {
19 | return (
20 | nextProps.time !== this.props.time
21 | );
22 | }
23 |
24 | render() {
25 | let {time, chain_time} = this.props;
26 | var offset_mills = chain_time ? ChainStore.getEstimatedChainTimeOffset() : 0
27 | if (!time) {
28 | return null;
29 | }
30 |
31 | if (typeof time === "string" && time.indexOf("+") === -1) {
32 | time += "+00:00";
33 | }
34 |
35 | let timePassed = Math.round(( new Date().getTime() - new Date(time).getTime() + offset_mills ) / 1000);
36 | let interval;
37 |
38 | if (timePassed < 60) { // 60s
39 | interval = 500; // 0.5s
40 | } else if (timePassed < 60 * 60) { // 1 hour
41 | interval = 60 * 500; // 30 seconds
42 | } else {
43 | interval = 60 * 60 * 500 // 30 minutes
44 | }
45 |
46 | return (
47 |
52 | );
53 |
54 | }
55 | }
56 |
57 | export default TimeAgo;
--------------------------------------------------------------------------------
/app/components/Utility/intlData.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | formats: {
3 | "date": {
4 | full: {
5 | second: "numeric",
6 | minute: "numeric",
7 | hour: "numeric",
8 | day: "numeric",
9 | month: "long",
10 | year: "numeric"
11 | },
12 | short: {
13 | //second: "numeric",
14 | //minute: "numeric",
15 | //hour: "numeric",
16 | day: "numeric",
17 | month: "numeric",
18 | year: "numeric"
19 | },
20 | time: {
21 | second: "numeric",
22 | minute: "numeric",
23 | hour: "numeric"
24 | }
25 | }
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/app/components/containers/GlobalSettingContainer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/25.
3 | */
4 | import React from "react";
5 | import GlobalSetting from "../GlobalSetting";
6 | import SettingsStore from "../../stores/SettingsStore";
7 | import IntlStore from "../../stores/IntlStore";
8 | import AltContainer from "alt-container";
9 |
10 | class GlobalSettingContainer extends React.Component {
11 |
12 | render() {
13 |
14 | return (
15 | {
19 | return SettingsStore.getState().settings;
20 | },
21 | defaults: () => {
22 | return SettingsStore.getState().defaults;
23 | },
24 | localesObject: () => {
25 | return IntlStore.getState().localesObject;
26 | }
27 | }}
28 | >
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default GlobalSettingContainer;
--------------------------------------------------------------------------------
/app/components/dashboard/AssetsItem.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/13.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import ChainTypes from "../Utility/ChainTypes";
7 | import utils from "../../../common/utils";
8 | import assetUtils from "../../../common/asset_utils";
9 | import BindToChainState from "../Utility/BindToChainState";
10 | import connectToStores from "alt-utils/lib/connectToStores";
11 | import AssetName from "../Utility/AssetName";
12 |
13 | //actions
14 | import MarketsActions from "../../actions/MarketsActions";
15 |
16 | //stores
17 | import MarketsStore from "../../stores/MarketsStore";
18 |
19 | class AssetsItem extends BaseComponent {
20 | static propTypes = {
21 | quote: ChainTypes.ChainAsset.isRequired,
22 | base: ChainTypes.ChainAsset.isRequired,
23 | invert: React.PropTypes.bool
24 | };
25 |
26 | static defaultProps = {
27 | invert: true
28 | };
29 |
30 | constructor(props) {
31 | super(props);
32 | this.statsInterval = null;
33 | }
34 |
35 | shouldComponentUpdate(nextProps) {
36 | return (
37 | !utils.are_equal_shallow(nextProps, this.props)
38 | );
39 | }
40 |
41 | componentWillMount() {
42 | MarketsActions.getMarketStats.defer(this.props.base, this.props.quote);
43 | this.statsChecked = new Date();
44 | this.statsInterval = setInterval(MarketsActions.getMarketStats.bind(this, this.props.quote, this.props.base), 35 * 1000);
45 | }
46 |
47 | componentWillUnmount() {
48 | clearInterval(this.statsInterval);
49 | }
50 |
51 | onClickHandler(e) {
52 | e.preventDefault();
53 | this.context.router.push(`/transaction/${this.props.quote.get("symbol")}_${this.props.base.get("symbol")}`);
54 | /*
55 | this.context.router.push({
56 | pathname: '/transaction',
57 | state: {baseAsset: this.props.base.get("symbol"), quoteAsset: this.props.quote.get("symbol")}
58 | });
59 | */
60 | }
61 |
62 | render() {
63 | let {base, quote, marketStats} = this.props;
64 |
65 | function getImageName(asset) {
66 | let symbol = asset.get("symbol");
67 | if (symbol === "OPEN.BTC") return symbol.replace('.', '-');
68 | let imgName = asset.get("symbol").split(".");
69 | return imgName.length === 2 ? imgName[1] : imgName[0];
70 | }
71 |
72 | let desc = assetUtils.parseDescription(base.getIn(["options", "description"]));
73 | let imgName = getImageName(quote);
74 | let marketID = quote.get("symbol") + "_" + base.get("symbol");
75 | let stats = marketStats.get(marketID);
76 | let gainClass = "def-label";
77 | if (stats) {
78 | let change = parseFloat(stats.change);
79 | gainClass = change > 0 ? "green-label" : change < 0 ? "orange-label" : "def-label";
80 | }
81 | /*
82 | if (imgName === "BTS" || imgName === "CNY" || imgName === "USD" || imgName === "EUR") {
83 | imgName = getImageName(quote);
84 | }
85 | */
86 |
87 | let unitPrice = (!stats || !stats.close) ? null : utils.format_price(
88 | stats.close.quote.amount,
89 | quote,
90 | stats.close.base.amount,
91 | base,
92 | true,
93 | this.props.invert
94 | );
95 | //处理价格小数
96 | let precision = 6;
97 | if (["BTC", "OPEN.BTC", "TRADE.BTC", "GOLD", "SILVER"].indexOf(base.get("symbol")) !== -1) {
98 | precision = 8;
99 | }
100 | let ups = (unitPrice) ? unitPrice.replace(',', '') : '';
101 | if (!isNaN(ups)) {
102 | unitPrice = utils.format_number(ups, ups > 1000 ? 0 : ups > 10 ? 2 : precision, false);
103 | }
104 | return (
105 |
106 |
107 |
108 |
110 |
113 |
117 |
118 |
119 |
120 | );
121 | }
122 | }
123 |
124 | AssetsItem = BindToChainState(AssetsItem);
125 |
126 | class AssetsItemWrapper extends React.Component {
127 | static getPropsFromStores() {
128 | return {
129 | marketStats: MarketsStore.getState().allMarketStats
130 | };
131 | }
132 |
133 | static getStores() {
134 | return [MarketsStore];
135 | }
136 |
137 | render() {
138 | return (
139 |
140 | );
141 | }
142 | }
143 |
144 | export default connectToStores(AssetsItemWrapper);
--------------------------------------------------------------------------------
/app/components/dashboard/Dashboard.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/13.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import Immutable from "immutable";
7 | import connectToStores from 'alt-utils/lib/connectToStores';
8 | import utils from "../../../common/utils";
9 |
10 | //组件
11 | import AssetsItem from "./AssetsItem";
12 | import AccountList from "./AccountList";
13 |
14 | //strores
15 | import {ChainStore} from "bitsharesjs";
16 | import AccountStore from "../../stores/AccountStore";
17 |
18 | class Dashboard extends BaseComponent {
19 | static getPropsFromStores() {
20 | return {linkedAccounts: AccountStore.getState().linkedAccounts};
21 | }
22 |
23 | static getStores() {
24 | return [AccountStore];
25 | }
26 |
27 | constructor(props) {
28 | super(props);
29 | this.state = {
30 | mainMarkets: [
31 | ["BTS", "CNY",false],
32 | ["BTS", "USD",false],
33 | ["BTS", "EUR",false]
34 | , ["OPEN.BTC", "BTS"],
35 | ["OPEN.BTC", "CNY"],
36 | ["OPEN.BTC", "USD"]
37 | ]
38 | };
39 | }
40 |
41 | shouldComponentUpdate(nextProps, nextState) {
42 | return (
43 | !utils.are_equal_shallow(nextState.mainMarkets, this.state.mainMarkets) ||
44 | nextProps.linkedAccounts !== this.props.linkedAccounts
45 | );
46 | }
47 |
48 |
49 | render() {
50 | let {linkedAccounts} = this.props;
51 | let names = linkedAccounts.toArray().sort();
52 |
53 | let markets = this.state.mainMarkets.map((item, index) => {
54 | if (index < 3)
55 | return (
56 |
62 | );
63 | });
64 | let markets2 = this.state.mainMarkets.map((item, index) => {
65 | if (index > 2)
66 | return (
67 |
73 | );
74 | });
75 |
76 | return (
77 |
78 |
79 | {markets}
80 |
81 |
82 | {markets2}
83 |
84 |
85 |
86 | );
87 | }
88 | }
89 |
90 | export default connectToStores(Dashboard);
91 |
--------------------------------------------------------------------------------
/app/components/form/XNFullButton.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/11.
3 | */
4 | import React from "react";
5 |
6 | export default class XNFullButton extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | }
10 |
11 | onClick(e) {
12 | this.props.onClick && this.props.onClick(e);
13 | }
14 |
15 | render() {
16 | let icon = null;
17 | if (this.props.isShowIcon) icon = >;
18 | return (
19 |
20 |
21 | {icon}
22 |
23 | );
24 | }
25 | }
26 |
27 | XNFullButton.propTypes = {
28 | label: React.PropTypes.string,
29 | isShowIcon: React.PropTypes.bool,
30 | onClick: React.PropTypes.func
31 | };
32 | XNFullButton.defaultProps = {
33 | label: "",
34 | isShowIcon: true,
35 | onClick: null
36 | };
--------------------------------------------------------------------------------
/app/components/form/XNFullText.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/25.
3 | */
4 |
5 | import React from "react";
6 |
7 | export default class XNFullText extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {value: props.value};
11 | }
12 |
13 | onTextChange(e) {
14 | this.setState({value: e.target.value});
15 | this.props.onChange && this.props.onChange(e.target.value);
16 | }
17 |
18 | render() {
19 | return (
20 |
21 |
22 |
23 |
24 | );
25 | }
26 | }
27 |
28 | XNFullText.propTypes = {
29 | label: React.PropTypes.string,
30 | value: React.PropTypes.number,
31 | type: React.PropTypes.string,
32 | onChange: React.PropTypes.func
33 | };
34 | XNFullText.defaultProps = {
35 | label: "",
36 | value: 0,
37 | type: 'text',
38 | onChange: null
39 | };
--------------------------------------------------------------------------------
/app/components/form/XNSelect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/7.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 |
7 | class XNSelect extends BaseComponent {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | value: props.value,
12 | data: props.data,//[{text: "中文简体", value: "zh"}, {text: "英文", value: "en"}],
13 | isShowList: false//是否弹出选择列表
14 | };
15 | this.onDocumentClick = this.onDocumentClick.bind(this);
16 | }
17 |
18 | stopEvent(e) {
19 | e.nativeEvent.stopImmediatePropagation();
20 | e.stopPropagation();
21 | e.preventDefault();
22 | }
23 |
24 | componentDidMount() {
25 |
26 | }
27 |
28 | componentWillReceiveProps(nextProps) {
29 | if (nextProps.value !== this.state.value || nextProps.data !== this.state.data) {
30 | this.setState({value: nextProps.value, data: nextProps.data});
31 | }
32 | }
33 |
34 | componentDidUpdate(prevProps, prevState) {
35 | if (this.state.isShowList) {
36 | document.addEventListener('click', this.onDocumentClick, false);
37 | } else {
38 | document.removeEventListener('click', this.onDocumentClick, false);
39 | }
40 | }
41 |
42 | componentWillUnmount() {
43 | }
44 |
45 | onDocumentClick(e) {
46 | //console.debug('onDocumentClick', e)
47 | this.setState({isShowList: false});
48 | }
49 |
50 | openList(e) {
51 | //console.debug('openList')
52 | let flag = !this.state.isShowList;
53 | this.setState({isShowList: flag});
54 | }
55 |
56 | //单击行事件
57 | onItemClick(d, e) {
58 | e.nativeEvent.stopImmediatePropagation();
59 | let oldVal = this.state.value;
60 | this.setState({value: d.text});
61 | if (this.props.onChange !== null && oldVal !== d.value) {
62 | this.props.onChange(d);
63 | }
64 | }
65 |
66 | //删除行事件
67 | onDelItemClick(item, index, e) {
68 | this.stopEvent(e);
69 | //console.debug('onDelItemClick', item);
70 | this.props.onDeleteItem && this.props.onDeleteItem(item, index);
71 | }
72 |
73 | //添加事件
74 | onAddClick(e) {
75 | this.stopEvent(e);
76 | let newItem = this.refs.newItem.value;
77 | //console.debug('onAddClick', newItem);
78 | this.props.onAddItem && this.props.onAddItem(newItem);
79 | }
80 |
81 | onInputClick(e) {
82 | this.stopEvent(e);
83 | }
84 |
85 | render() {
86 | let list = this.state.isShowList === false ? null : (
87 |
88 |
89 | {this.state.data.map((item, i) => {
90 | return (-
91 | {item.text}
92 | {
93 | this.props.isDelete === false ? null : (
94 | '
95 | )
96 | }
97 |
);
98 | })}
99 |
100 | {
101 | this.props.isAdd === false ? null : (
102 |
103 | {this.formatMessage('settings_add')}
105 |
106 | )
107 | }
108 |
109 | );
110 | return (
111 |
112 |
113 |
114 | {this.state.value}
115 |
116 |
>
117 | {list}
118 |
119 | );
120 | }
121 | }
122 | XNSelect.propTypes = {
123 | label: React.PropTypes.string,
124 | onChange: React.PropTypes.func,
125 | onDeleteItem: React.PropTypes.func,
126 | onAddItem: React.PropTypes.func,
127 | isAdd: React.PropTypes.bool,
128 | isDelete: React.PropTypes.bool,
129 | data: React.PropTypes.array,
130 | value: React.PropTypes.string
131 | };
132 | XNSelect.defaultProps = {
133 | label: "显示名称",
134 | onChange: null,
135 | onDeleteItem: null,
136 | onAddItem: null,
137 | isAdd: false,
138 | isDelete: false,
139 | data: [],
140 | value: ""
141 | };
142 |
143 | export default XNSelect;
--------------------------------------------------------------------------------
/app/components/form/XNSwitch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/8.
3 | */
4 | import React, {Component} from "react";
5 |
6 | export default class XNSwitch extends Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {value: props.value};
10 | }
11 |
12 | onSpanClick(e) {
13 | let value = !this.state.value;
14 | this.setState({value: value});
15 | this.props.onChange && this.props.onChange(value);
16 | }
17 |
18 | render() {
19 | let openClass = this.state.value ? "open" : "close";
20 | return (
21 |
22 |
23 |
24 |
25 | );
26 | }
27 | }
28 |
29 | XNSwitch.propTypes = {
30 | label: React.PropTypes.string,
31 | value: React.PropTypes.bool,
32 | onChange: React.PropTypes.func
33 | };
34 | XNSwitch.defaultProps = {
35 | label: "显示名称",
36 | value: false,
37 | onChange: null
38 | };
--------------------------------------------------------------------------------
/app/components/layout/Confirm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/3/17.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import AltContainer from "alt-container";
7 | import ConfirmStore from "../../stores/layout/ConfirmStore";
8 | import ConfirmActions from "../../actions/layout/ConfirmActions";
9 | import Modal from "./Modal";
10 |
11 | class Confirm extends BaseComponent {
12 |
13 | constructor(props) {
14 | super(props);
15 | this.state = this.initState();
16 | this.close = this.close.bind(this);
17 | }
18 |
19 | initState() {
20 | return {show: false};
21 | }
22 |
23 | componentWillReceiveProps(props) {
24 | if (props.show !== undefined) {
25 | let show = props.show ? true : false;
26 | this.setState({show: show});
27 | }
28 | }
29 |
30 | reset() {
31 | ConfirmActions.reset();
32 | }
33 |
34 | close() {
35 | this.setState({show: false});
36 | }
37 |
38 | okClick(e) {
39 | this.close();
40 | if (this.props.onOK) {
41 | this.props.onOK();
42 | }
43 | this.reset();
44 | }
45 |
46 | cancelClick(e) {
47 | this.close();
48 | if (this.props.onCancel) {
49 | this.props.onCancel();
50 | }
51 | this.reset();
52 | }
53 |
54 | render() {
55 | let {title, msg, height, showCancelButton} = this.props;
56 | return (
57 |
58 |
59 | {title}
60 |
61 |
62 | {msg}
63 |
64 |
65 |
67 | {!showCancelButton ? null :
68 |
70 | }
71 |
72 |
73 |
74 | );
75 | }
76 | }
77 |
78 | class ConfirmContainer extends React.Component {
79 | render() {
80 | return (
81 |
82 |
83 |
84 | )
85 | }
86 | }
87 | export default ConfirmContainer
--------------------------------------------------------------------------------
/app/components/layout/Modal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/1/29.
3 | */
4 | import React from "react";
5 |
6 | const {PropTypes, Component} = React;
7 | const propTypes = {
8 | width: PropTypes.number,
9 | height: PropTypes.number,
10 | measure: PropTypes.string,
11 | visible: PropTypes.bool,
12 | showMask: PropTypes.bool,
13 | showCloseButton: PropTypes.bool,
14 | animation: PropTypes.string,
15 | duration: PropTypes.number,
16 | className: PropTypes.string,
17 | customStyles: PropTypes.object,
18 | customMaskStyles: PropTypes.object,
19 | onClose: PropTypes.func.isRequired,
20 | outsideClose: PropTypes.bool //是否可点窗口外关闭
21 | };
22 |
23 | const defaultProps = {
24 | width: 6,
25 | height: 3.6,
26 | measure: 'rem',
27 | visible: false,
28 | showMask: true,
29 | showCloseButton: true,
30 | animation: 'flip',
31 | duration: 300,
32 | className: '',
33 | customStyles: {},
34 | customMaskStyles: {},
35 | outsideClose: false
36 | };
37 |
38 | const Dialog = props => {
39 | const _onClose = () => {
40 | if (props.onClose) props.onClose(false);
41 | };
42 | const className = `modal-dialog modal-${props.animation}-${props.animationType}`;
43 | const CloseButton = props.showCloseButton ? : null;
44 | const {width, height, measure, duration, customStyles} = props;
45 | const style = {
46 | width: width + measure,
47 | height: height + measure,
48 | animationDuration: duration + 'ms',
49 | WebkitAnimationDuration: duration + 'ms'
50 | };
51 |
52 | const mergedStyles = Object.assign(style, customStyles)
53 |
54 | return (
55 |
56 | {CloseButton}
57 | {props.children}
58 |
59 | )
60 | };
61 |
62 | class Modal extends Component {
63 |
64 | constructor(props) {
65 | super(props);
66 |
67 | this.animationEnd = this.animationEnd.bind(this);
68 | this.state = {
69 | isShow: false,
70 | animationType: 'leave'
71 | };
72 | }
73 |
74 | componentDidMount() {
75 | if (this.props.visible) {
76 | this.enter();
77 | }
78 | }
79 |
80 | componentWillReceiveProps(nextProps) {
81 | if (!this.props.visible && nextProps.visible) {
82 | this.enter();
83 | } else if (this.props.visible && !nextProps.visible) {
84 | this.leave();
85 | }
86 | }
87 |
88 | enter() {
89 | this.setState({
90 | isShow: true,
91 | animationType: 'enter'
92 | });
93 | }
94 |
95 | leave() {
96 | this.setState({
97 | animationType: 'leave'
98 | });
99 | }
100 |
101 | animationEnd() {
102 | if (this.state.animationType === 'leave') {
103 | this.setState({
104 | isShow: false
105 | });
106 | }
107 | }
108 |
109 | onMaskClick(e) {
110 | if (this.props.outsideClose && this.props.onClose) {
111 | this.props.onClose(false);
112 | }
113 | }
114 |
115 | render() {
116 | const mask = this.props.showMask ?
117 | : null;
119 | const style = {
120 | display: this.state.isShow ? 'block' : 'none',
121 | WebkitAnimationDuration: this.props.duration + 'ms',
122 | animationDuration: this.props.duration + 'ms'
123 | };
124 |
125 | return (
126 |
130 | {mask}
131 |
134 |
135 | )
136 | }
137 | }
138 |
139 | Modal.propTypes = propTypes;
140 | Modal.defaultProps = defaultProps;
141 |
142 | export default Modal;
--------------------------------------------------------------------------------
/app/components/transaction/CanBuySell.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/3/1.
3 | */
4 | import React from "react";
5 | import ChainTypes from "../Utility/ChainTypes";
6 | import BindToChainState from "../Utility/BindToChainState";
7 | import utils from "../../../common/utils";
8 | import {FormattedNumber} from "react-intl";
9 |
10 | class CanBuySell extends React.Component {
11 | static propTypes = {
12 | balance: ChainTypes.ChainObject.isRequired,
13 | price: React.PropTypes.number,
14 | fromAsset: ChainTypes.ChainAsset.isRequired,
15 | isAsk: React.PropTypes.bool
16 | };
17 | static defaultProps = {
18 | isAsk: false
19 | };
20 |
21 | render() {
22 | let {fromAsset, balance, price}=this.props;
23 | let balances = Number(balance.get("balance"));
24 | let value = 0;
25 | let cn = 'green';
26 | if (fromAsset && fromAsset.toJS) fromAsset = fromAsset.toJS();
27 | let precision = utils.get_asset_precision(fromAsset.precision);
28 | if (this.props.isAsk) {
29 | cn = 'orangeRed';
30 | value = (balances/precision) * price;
31 | } else {
32 | value = (balances/precision) / price;
33 | }
34 | let decimals = Math.max(0, fromAsset.precision);
35 |
36 | return (
37 |
44 | );
45 | }
46 | }
47 | export default BindToChainState(CanBuySell);
--------------------------------------------------------------------------------
/app/components/transaction/CurrentBalance.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/3/1.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import utils from "../../../common/utils";
7 | import BalanceComponent from "../Utility/BalanceComponent";
8 | import FormattedAsset from "../Utility/FormattedAsset";
9 | import CanBuySell from "./CanBuySell";
10 |
11 | class CurrentBalance extends BaseComponent {
12 | static propTypes = {
13 | isAsk: React.PropTypes.bool,
14 | latestPrice: React.PropTypes.number,
15 | onBalanceClick: React.PropTypes.func
16 | };
17 |
18 | static defaultProps = {
19 | isAsk: false,
20 | onBalanceClick: null
21 | };
22 |
23 | constructor(props) {
24 | super(props);
25 | }
26 |
27 | onBalanceClick(balance, e) {
28 | e.d
29 | if (balance != null && this.props.onBalanceClick) {
30 | this.props.onBalanceClick(balance);
31 | }
32 | }
33 |
34 | render() {
35 | let {quoteAsset, baseAsset, currentAccount, latestPrice, isAsk}=this.props;
36 | let base = null, quote = null, accountBalance = null, quoteBalance = null,
37 | baseBalance = null, coreBalance = null, quoteSymbol, baseSymbol, aSymbol, bSymbol, balance;
38 | if (quoteAsset.size && baseAsset.size && currentAccount.size) {
39 | base = baseAsset;
40 | quote = quoteAsset;
41 | baseSymbol = base.get("symbol");
42 | quoteSymbol = quote.get("symbol");
43 |
44 | accountBalance = currentAccount.get("balances").toJS();
45 |
46 | if (accountBalance) {
47 | for (let id in accountBalance) {
48 | if (id === quote.get("id")) {
49 | quoteBalance = accountBalance[id];
50 | }
51 | if (id === base.get("id")) {
52 | baseBalance = accountBalance[id];
53 | }
54 | if (id === "1.3.0") {
55 | coreBalance = accountBalance[id];
56 | }
57 | }
58 | }
59 | }
60 | if (isAsk) {
61 | aSymbol = quote;
62 | bSymbol = base;
63 | balance = quoteBalance;
64 | } else {
65 | aSymbol = base;
66 | bSymbol = quote;
67 | balance = baseBalance;
68 | }
69 |
70 | return (
71 |
72 |
73 |
{this.formatMessage("transaction_currentBalance", {symbol: utils.getAssetName(aSymbol)})}:
74 |
80 |
81 |
{this.formatMessage(isAsk ? "transaction_canSell" : "transaction_canBuy", {symbol: utils.getAssetName(bSymbol)})}:
82 | {balance == null ? 0 :
83 |
85 | }
86 |
87 |
88 |
89 |
90 | );
91 | }
92 | }
93 |
94 | export default CurrentBalance;
--------------------------------------------------------------------------------
/app/components/transaction/OrderBook.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/3/6.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import utils from "../../../common/utils";
7 | import market_utils from "../../../common/market_utils";
8 | import AssetName from "../Utility/AssetName";
9 |
10 | //import PriceText from "../Utility/PriceText";
11 |
12 | class OrderBookRow extends BaseComponent {
13 | constructor(props) {
14 | super(props);
15 | }
16 |
17 | shouldComponentUpdate(nextProps) {
18 | if (nextProps.order.market_base !== this.props.order.market_base) return false;
19 | return (
20 | nextProps.order.ne(this.props.order) ||
21 | nextProps.index !== this.props.index
22 | );
23 | }
24 |
25 | render() {
26 | let {order, quote, base} = this.props;
27 | const isBid = order.isBid();
28 | const isCall = order.isCall();
29 |
30 | //let price =
;
31 | let {price} = market_utils.parseOrder(order, base, quote);
32 |
33 | return (
34 |
35 | {utils.formatNumber(order[isBid ? "amountForSale" : "amountToReceive"]().getAmount({real: true}), 4)}
36 | {utils.formatNumber(order[isBid ? "amountToReceive" : "amountForSale"]().getAmount({real: true}), 4)}
37 | {utils.formatNumber(price.full, 4)}
38 |
39 | );
40 | }
41 | }
42 |
43 | class OrderBook extends BaseComponent {
44 | static propTypes = {
45 | isAsk: React.PropTypes.bool
46 | };
47 | static defaultProps = {
48 | isAsk: false
49 | };
50 |
51 | constructor(props) {
52 | super(props);
53 | }
54 |
55 | componentDidMount() {
56 | let uplist = this.refs.uplist;
57 | setTimeout(() => {
58 | uplist.scrollTop = uplist.scrollHeight;
59 | }, 200);
60 | }
61 |
62 | render() {
63 | let {
64 | combinedBids, combinedAsks, highestBid, lowestAsk, quote, base,
65 | totalAsks, totalBids, quoteSymbol, baseSymbol, isAsk
66 | } = this.props;
67 |
68 | let bidRows = null, askRows = null;
69 | if (base && quote) {
70 | let tempBids = combinedBids.filter(a => {
71 | return a.getPrice() >= highestBid.getPrice() / 5;
72 | });
73 | if (isAsk) {
74 | tempBids.sort((a, b) => {
75 | return a.getPrice() - b.getPrice();
76 | });
77 | }
78 | bidRows = tempBids.map((order, index) => {
79 | return (
80 |
88 | );
89 | });
90 |
91 | let tempAsks = combinedAsks
92 | .filter(a => {
93 | return a.getPrice() <= lowestAsk.getPrice() * 5;
94 | });
95 | if (!isAsk) {
96 | tempAsks.sort((a, b) => {
97 | return b.getPrice() - a.getPrice();
98 | });
99 | }
100 | askRows = tempAsks.map((order, index) => {
101 | return (
102 |
111 | );
112 | });
113 | }
114 |
115 | let cnbid, cnask;
116 | if (!isAsk) {
117 | cnbid = 'depth-list-buy scroll';
118 | cnask = 'depth-list-sell scroll';
119 | askRows.splice(0, askRows.length - 50);
120 | bidRows.splice(50, bidRows.length);
121 | } else {
122 | cnbid = 'depth-list-sell scroll';
123 | cnask = 'depth-list-buy scroll';
124 | bidRows.splice(0, bidRows.length - 50);
125 | askRows.splice(50, askRows.length);
126 | }
127 |
128 |
129 | return (
130 |
131 |
132 |
133 |
134 |
{this.formatMessage("transaction_depthPrice")}
135 |
136 |
137 | {(!isAsk) ? askRows : bidRows}
138 |
139 |
140 |
141 | {(!isAsk) ? bidRows : askRows}
142 |
143 |
144 | );
145 | }
146 | }
147 |
148 | export default OrderBook;
--------------------------------------------------------------------------------
/app/components/transaction/TabComponent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/14.
3 | */
4 | import React from "react";
5 |
6 | class TabComponent extends React.Component {
7 |
8 | static propTypes = {
9 | data: React.PropTypes.array.isRequired
10 | };
11 | static contextTypes = {
12 | router: React.PropTypes.object
13 | };
14 |
15 | constructor(props) {
16 | super(props);
17 | this.state = {currentIndex: 0};
18 | }
19 |
20 | componentWillMount() {
21 | //console.debug('location:', this.context.router.location)
22 | let index = 0;
23 | this.props.data.forEach((x, i) => {
24 | if (x.url === this.context.router.location.pathname)
25 | index = i;
26 | });
27 | this.setState({currentIndex: index});
28 | }
29 |
30 | clickHandle(index, url) {
31 | //console.debug(this.context.router);
32 | this.setState({currentIndex: index});
33 | //let path = this.context.router.location.pathname + "/" + url;
34 | let s = this.context.router.location.state;
35 | //this.context.router.push(url,s);
36 | this.context.router.push({pathname: url, state: s});
37 | }
38 |
39 | render() {
40 | return (
41 |
42 |
43 |
44 | {this.props.data.map((item, i) => {
45 | return (
{item.name}
);
47 | })}
48 |
49 |
50 |
51 |
52 | {React.cloneElement(this.props.children, {...this.props})}
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default TabComponent;
--------------------------------------------------------------------------------
/app/components/transaction/Transaction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/14.
3 | */
4 | import React, {PropTypes} from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import TabComponent from "./TabComponent";
7 | import utils from "../../../common/utils";
8 | import market_utils from "../../../common/market_utils";
9 |
10 | //组件
11 | import AssetName from "../Utility/AssetName";
12 |
13 | class Transaction extends BaseComponent {
14 | static propTypes = {
15 | marketCallOrders: PropTypes.object.isRequired,
16 | activeMarketHistory: PropTypes.object.isRequired,
17 | priceData: PropTypes.array.isRequired,
18 | volumeData: PropTypes.array.isRequired
19 | };
20 |
21 | static defaultProps = {
22 | marketCallOrders: [],
23 | activeMarketHistory: {},
24 | priceData: [],
25 | volumeData: []
26 | };
27 |
28 | constructor(props) {
29 | super(props);
30 | }
31 |
32 | render() {
33 | let marketID = this.props.params.marketID;
34 | let latestPrice = 0;
35 | let aVolume = 0;
36 | let bVolume = 0;
37 | let tabData = [
38 | {name: this.formatMessage("transaction_pay"), url: `/transaction/${marketID}/buy`},
39 | {name: this.formatMessage("transaction_sell"), url: `/transaction/${marketID}/sell`},
40 | {name: this.formatMessage("transaction_orders"), url: `/transaction/${marketID}/orders`},
41 | {name: this.formatMessage("transaction_history"), url: `/transaction/${marketID}/history`}
42 | ];
43 |
44 | let {activeMarketHistory, quoteAsset, baseAsset, marketStats} = this.props;
45 | let base = null, quote = null, quoteBalance = null,
46 | baseBalance = null, coreBalance = null, quoteSymbol, baseSymbol, changeClass;
47 | if (quoteAsset.size && baseAsset.size) {
48 | base = baseAsset;
49 | quote = quoteAsset;
50 | baseSymbol = base.get("symbol");
51 | quoteSymbol = quote.get("symbol");
52 | }
53 |
54 |
55 | //最新价格
56 | if (activeMarketHistory.size) {
57 | // 取得最后成交订单
58 | let latest_two = activeMarketHistory.take(3);
59 | let latest = latest_two.first();
60 | let second_latest = latest_two.last();
61 | let paysAsset, receivesAsset, isAsk = false;
62 | if (latest.pays.asset_id === base.get("id")) {
63 | paysAsset = base;
64 | receivesAsset = quote;
65 | isAsk = true;
66 | } else {
67 | paysAsset = quote;
68 | receivesAsset = base;
69 | }
70 | let flipped = base.get("id").split(".")[2] > quote.get("id").split(".")[2];
71 | latestPrice = market_utils.parse_order_history(latest, paysAsset, receivesAsset, isAsk, flipped);
72 |
73 | isAsk = false;
74 | if (second_latest) {
75 | if (second_latest.pays.asset_id === base.get("id")) {
76 | paysAsset = base;
77 | receivesAsset = quote;
78 | isAsk = true;
79 | } else {
80 | paysAsset = quote;
81 | receivesAsset = base;
82 | }
83 |
84 | let oldPrice = market_utils.parse_order_history(second_latest, paysAsset, receivesAsset, isAsk, flipped);
85 | changeClass = latestPrice.full === oldPrice.full ? "" : latestPrice.full - oldPrice.full > 0 ? "green" : "orangeRed";
86 | }
87 | }
88 |
89 | //成交量
90 | const volumeBase = marketStats.get("volumeBase");
91 | const volumeQuote = marketStats.get("volumeQuote");
92 | aVolume = utils.format_volume(volumeBase);
93 | bVolume = utils.format_volume(volumeQuote);
94 |
95 | return (
96 |
97 |
98 |
99 |
{this.formatMessage("transaction_latest")} :
100 |
{utils.price_text(latestPrice.full, quoteAsset, baseAsset)}
102 |
103 |
104 | {quoteAsset ? / : null}
105 |
106 |
107 |
108 |
{this.formatMessage("transaction_volume")} :
109 |
{aVolume}
110 |
{bVolume}
111 |
112 |
113 |
114 |
115 | );
116 | }
117 | }
118 |
119 | export default Transaction;
--------------------------------------------------------------------------------
/app/components/wallet/AccountSelectInput.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/2/19.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import utils from "../../../common/utils";
7 | import BindToChainState from "../Utility/BindToChainState";
8 | import AccountImage from "../Utility/AccountImage";
9 | import ChainTypes from "../Utility/ChainTypes";
10 | import {ChainStore, PublicKey, ChainValidation} from "bitsharesjs";
11 |
12 | class AccountSelectInput extends BaseComponent {
13 | static propTypes = {
14 | error: React.PropTypes.element,
15 | onChange: React.PropTypes.func, // 用户输入时调用
16 | onAccountChanged: React.PropTypes.func, // 账户选择变化时调用
17 | accountName: React.PropTypes.string, // 用户输入的当前账户名
18 | account: ChainTypes.ChainAccount, // 绑定的账户对象
19 | lable: React.PropTypes.string,//显示标题
20 | placeholder: React.PropTypes.string,//输入框提示内容
21 | }
22 |
23 | constructor(props) {
24 | super(props);
25 | }
26 |
27 | componentDidMount() {
28 | if (this.props.onAccountChanged && this.props.account)
29 | this.props.onAccountChanged(this.props.account);
30 | }
31 |
32 | componentWillReceiveProps(newProps) {
33 | if ((this.props.onAccountChanged && newProps.account) && newProps.account !== this.props.account)
34 | this.props.onAccountChanged(newProps.account);
35 | }
36 |
37 | getNameType(value) {
38 | if (!value) return null;
39 | if (value[0] === "#" && utils.is_object_id("1.2." + value.substring(1))) return "id";
40 | if (ChainValidation.is_account_name(value, true)) return "name";
41 | if (this.props.allowPubKey && PublicKey.fromPublicKeyString(value)) return "pubkey";
42 | return null;
43 | }
44 |
45 | onInputChange(e) {
46 | let value = e.target.value.trim();
47 | if (!this.props.allowUppercase) {
48 | value = value.toLowerCase();
49 | }
50 | let newValue = value.match(/(?:#\/account\/)(.*)(?:\/overview)/);
51 | if (newValue) value = newValue[1];
52 |
53 | if (this.props.onChange && value !== this.props.accountName) this.props.onChange(value);
54 | }
55 |
56 | render() {
57 | let type = this.getNameType(this.props.accountName);
58 | let lookup_display;
59 | if (this.props.allowPubKey) {
60 | if (type === "pubkey") lookup_display = "Public Key";
61 | } else if (this.props.account) {
62 | if (type === "name") lookup_display = "#" + this.props.account.get("id").substring(4);
63 | else if (type === "id") lookup_display = this.props.account.get("name");
64 | }
65 | let member_status = null;
66 | if (this.props.account)
67 | member_status = this.formatMessage("transfer_member_" + ChainStore.getAccountMemberStatus(this.props.account));
68 | return (
69 |
70 |
71 |
72 |
73 |
74 | {this.props.lable}{member_status} {lookup_display}
75 |
76 |
77 |
80 |
81 |
82 |
{this.props.error}
83 |
84 | );
85 | }
86 | }
87 | export default BindToChainState(AccountSelectInput, {keep_updating: true})
--------------------------------------------------------------------------------
/app/components/wallet/ChangePassword.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/12.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import PasswordInput from "../wallet/PasswordInput";
7 | import TextLoading from "../TextLoading";
8 |
9 | //stores
10 | import WalletDb from "../../stores/WalletDb";
11 |
12 | //actions
13 | import NotificationActions from "../../actions/NotificationActions";
14 |
15 | class ChangePassword extends BaseComponent {
16 | constructor(props) {
17 | super(props);
18 | this.state = this.initState();
19 | }
20 |
21 | initState() {
22 | return {error_message: null, oldPwd: '', newPwd: '', loading: false};
23 | }
24 |
25 | onOldPwdChange(e) {
26 | this.setState({oldPwd: e.target.value});
27 | }
28 |
29 | onNewPwdChange(e) {
30 | if (e.error) {
31 | this.setState({error_message: e.error, newPwd: null});
32 | } else {
33 | this.setState({error_message: null, newPwd: e.value});
34 | }
35 | }
36 |
37 | onAccept(e) {
38 | e.preventDefault();
39 | let {oldPwd, newPwd} = this.state;
40 | this.setState({loading: true});
41 | if (WalletDb.validatePassword(oldPwd)) {
42 | WalletDb.changePassword(oldPwd, newPwd, true)
43 | .then(() => {
44 | NotificationActions.success("Password changed");
45 | this.setState(this.initState());
46 | this.context.router.push("/settings/backup");
47 | })
48 | .catch(error => {
49 | console.error(error);
50 | NotificationActions.error("Unable to change password: " + error);
51 | this.setState({loading: false});
52 | });
53 | } else {
54 | NotificationActions.error("Invalid Password");
55 | }
56 | }
57 |
58 | render() {
59 | return (
60 |
61 |
62 |
{this.formatMessage('wallet_oldPassword')}
63 |
65 |
66 |
67 |
68 | {this.state.loading ? :
69 |
71 | }
72 |
73 | {this.state.error_message === null ? null :
74 |
75 | {this.state.error_message}
76 |
77 | }
78 |
79 | );
80 | }
81 | }
82 |
83 | export default ChangePassword;
--------------------------------------------------------------------------------
/app/components/wallet/PasswordInput.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/1/28.
3 | */
4 | import React, {PropTypes} from "react";
5 | import BaseComponent from "../BaseComponent";
6 |
7 | class PasswordInput extends BaseComponent {
8 |
9 | static propTypes = {
10 | onChange: PropTypes.func,
11 | onEnter: PropTypes.func,
12 | confirmation: PropTypes.bool,
13 | wrongPassword: PropTypes.bool,
14 | noValidation: PropTypes.bool
15 | };
16 |
17 | static defaultProps = {
18 | confirmation: false,
19 | wrongPassword: false,
20 | noValidation: false
21 | };
22 |
23 | constructor() {
24 | super();
25 | this.handleChange = this.handleChange.bind(this);
26 | this.onKeyDown = this.onKeyDown.bind(this);
27 | this.state = {value: "", error: null, wrong: false, doesnt_match: false};
28 | }
29 |
30 | value() {
31 | let node = this.refs.password;
32 | return node ? node.value : "";
33 | }
34 |
35 | clear() {
36 | this.refs.password.value = "";
37 | if (this.props.confirmation) this.refs.confirm_password.value = "";
38 | }
39 |
40 | focus() {
41 | this.refs.password.focus();
42 | }
43 |
44 | valid() {
45 | return !(this.state.error || this.state.wrong || this.state.doesnt_match) && this.state.value.length >= 8;
46 | }
47 |
48 | handleChange(e) {
49 | e.preventDefault();
50 | e.stopPropagation();
51 | const confirmation = this.props.confirmation ? this.refs.confirm_password.value : true;
52 | const password = this.refs.password.value;
53 | const doesnt_match = this.props.confirmation ? confirmation && password !== confirmation : false;
54 | if (!this.props.noValidation && !this.state.error && (password.length > 0 && password.length < 8))
55 | this.state.error = this.formatMessage('wallet_createErrMsg4');
56 | if(doesnt_match)
57 | this.state.error = this.formatMessage('wallet_createErrMsg5');
58 | if(this.state.wrong || this.props.wrongPassword)
59 | this.state.error = this.formatMessage('wallet_createErrMsg6');
60 | let state = {
61 | valid: !this.state.error && !this.state.wrong
62 | && !(this.props.confirmation && doesnt_match)
63 | && confirmation && password.length >= 8,
64 | value: password,
65 | error: this.state.error,
66 | doesnt_match
67 | };
68 | if (this.props.onChange) this.props.onChange(state);
69 | this.setState(state);
70 | this.setState({error:null});
71 | }
72 |
73 | onKeyDown(e) {
74 | if (this.props.onEnter && e.keyCode === 13) this.props.onEnter(e);
75 | }
76 |
77 | render() {
78 | return (
79 |
80 |
81 |
{this.formatMessage("wallet_password")}
82 |
86 |
87 | {!this.props.confirmation ? null :
88 |
89 |
{this.formatMessage("wallet_confirmPassword")}
90 |
94 |
95 | }
96 |
97 | );
98 | }
99 | }
100 | export default PasswordInput;
--------------------------------------------------------------------------------
/app/components/wallet/UnlockWallet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/12.
3 | */
4 |
5 | import React from "react";
6 | import BaseComponent from "../BaseComponent";
7 | import Modal from "../layout/Modal";
8 |
9 | import {Apis} from "bitsharesjs-ws";
10 | import AltContainer from "alt-container";
11 |
12 | //stores
13 | import WalletDb from "../../stores/WalletDb";
14 | import WalletUnlockStore from "../../stores/WalletUnlockStore";
15 | //actions
16 | import NotificationActions from "../../actions/NotificationActions";
17 | import WalletUnlockActions from "../../actions/WalletUnlockActions";
18 |
19 | class UnlockWallet extends BaseComponent {
20 | constructor(props) {
21 | super(props);
22 | this.state = this.getInitialState();
23 | this.onPasswordEnter = this.onPasswordEnter.bind(this);
24 | }
25 |
26 | getInitialState() {
27 | return {
28 | visible: false,
29 | password_error: null,
30 | password_input_reset: Date.now()
31 | }
32 | }
33 |
34 | componentWillReceiveProps(nextProps) {
35 | if (nextProps.resolve) {
36 | if (WalletDb.isLocked())
37 | this.show();
38 | else
39 | nextProps.resolve();
40 | }
41 | }
42 |
43 | show() {
44 | let wallet = WalletDb.getWallet();
45 | if (!wallet) {
46 | return;
47 | }
48 | if (Apis.instance().chain_id !== wallet.chain_id) {
49 | NotificationActions.error("This wallet was intended for a different block-chain; expecting " +
50 | wallet.chain_id.substring(0, 4).toUpperCase() + ", but got " +
51 | Apis.instance().chain_id.substring(0, 4).toUpperCase());
52 | return;
53 | }
54 | this.setState({visible: true});
55 | }
56 |
57 | hide(ok) {
58 | if (!ok) {
59 | WalletUnlockActions.cancel();
60 | }
61 | this.setState({visible: false, password_error: null});
62 |
63 | }
64 |
65 | onPasswordEnter(e) {
66 | e.preventDefault();
67 | let password = this.refs.password_input.value;
68 | this.setState({password_error: null});
69 | WalletDb.validatePassword(password || "", true);
70 | if (WalletDb.isLocked()) {
71 | this.setState({password_error: this.formatMessage("wallet_passwordErrMsg")});
72 | return false;
73 | }
74 | else {
75 | this.refs.password_input.value = "";
76 | this.props.resolve();
77 | WalletUnlockActions.change();
78 | this.setState({password_input_reset: Date.now(), password_error: null, visible: false});
79 | }
80 | return false;
81 | }
82 |
83 | render() {
84 | return (
85 |
86 |
87 | {this.formatMessage('transaction_confirm_unlock')}
88 |
89 |
90 |
91 |
{this.formatMessage('wallet_password')}
92 |
94 |
95 |
96 |
97 | {this.state.password_error}
98 |
99 |
100 |
102 |
103 |
104 |
105 | );
106 | }
107 | }
108 |
109 | class WalletUnlockModalContainer extends React.Component {
110 | render() {
111 | return (
112 |
113 |
114 |
115 | )
116 | }
117 | }
118 | export default WalletUnlockModalContainer
--------------------------------------------------------------------------------
/app/components/wallet/WalletManage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/1/11.
3 | */
4 | import React from "react";
5 | import BaseComponent from "../BaseComponent";
6 | import connectToStores from 'alt-utils/lib/connectToStores';
7 | import XNFullButton from "../form/XNFullButton";
8 | import XNSelect from "../form/XNSelect";
9 |
10 | //stores
11 | import WalletManagerStore from "../../stores/WalletManagerStore";
12 |
13 | //actions
14 | import WalletActions from "../../actions/WalletActions";
15 | import ConfirmActions from "../../actions/layout/ConfirmActions";
16 | import WalletUnlockActions from "../../actions/WalletUnlockActions";
17 |
18 | class WalletManage extends BaseComponent {
19 | static getPropsFromStores() {
20 | return WalletManagerStore.getState();
21 | }
22 |
23 | static getStores() {
24 | return [WalletManagerStore];
25 | }
26 |
27 | constructor(props) {
28 | super(props);
29 | this.state = {names: props.wallet_names};
30 | }
31 |
32 | componentWillReceiveProps(nextProps) {
33 | if (nextProps.wallet_names.size != this.props.wallet_names.size) {
34 | this.setState({names: nextProps.wallet_names});
35 | }
36 | }
37 |
38 | onImportKeyClick(e) {
39 | e.preventDefault();
40 | this.context.router.push("/settings/import-key");
41 | }
42 |
43 | onBackupClick(e) {
44 | e.preventDefault();
45 | this.context.router.push("/settings/backup");
46 | }
47 |
48 | onImportBackupClick(e) {
49 | e.preventDefault();
50 | this.context.router.push("/settings/import-backup");
51 | }
52 |
53 | onModifyPasswordClick(e) {
54 | e.preventDefault();
55 | this.context.router.push("/settings/change-password");
56 | }
57 |
58 | onWalletChange(item) {
59 | WalletActions.setWallet(item.value);
60 | }
61 |
62 | onDeleteWallet(item) {
63 | let names = this.props.wallet_names;
64 | let size = names.size;
65 | let current_wallet = this.props.current_wallet;
66 | let title = this.formatMessage('message_title');
67 | let msg = this.formatMessage('wallet_confirmDelete');
68 | WalletUnlockActions.unlock().then(() => {
69 | ConfirmActions.show(title, msg, () => {
70 | WalletManagerStore.onDeleteWallet(item.value);
71 | if (current_wallet === item.value) {
72 | if (size > 1) {
73 | let wn = null;
74 | names.forEach(name => {
75 | if (name !== item.value) {
76 | wn = name;
77 | }
78 | });
79 | if (wn) WalletActions.setWallet(wn);
80 | } else {
81 | setTimeout(() => {
82 | window.location.reload();
83 | }, 250);
84 | }
85 | }
86 | }, null, 3);
87 | });
88 | }
89 |
90 | render() {
91 | let wallets = [];
92 | //console.debug(this.props.wallet_names);
93 | this.state.names.forEach(name => {
94 | wallets.push({text: name, value: name});
95 | });
96 | let current_wallet = this.props.current_wallet;
97 | return (
98 |
99 |
104 |
106 |
108 |
110 |
112 |
113 | );
114 |
115 | }
116 | }
117 |
118 | export default connectToStores(WalletManage);
--------------------------------------------------------------------------------
/app/idb-helper.js:
--------------------------------------------------------------------------------
1 | var db
2 | var idb_helper
3 |
4 | module.exports = idb_helper = {
5 |
6 | set_graphene_db: database => {
7 | db = database
8 | },
9 |
10 | trx_readwrite: object_stores => {
11 | return db.transaction(
12 | [object_stores], "readwrite"
13 | )
14 | },
15 |
16 | on_request_end: (request) => {
17 | //return request => {
18 | return new Promise((resolve, reject) => {
19 | request.onsuccess = new ChainEvent(
20 | request.onsuccess, resolve, request).event
21 | request.onerror = new ChainEvent(
22 | request.onerror, reject, request).event
23 | })
24 | //}(request)
25 | },
26 |
27 | on_transaction_end: (transaction) => {
28 | return new Promise((resolve, reject) => {
29 | transaction.oncomplete = new ChainEvent(
30 | transaction.oncomplete, resolve).event
31 | transaction.onabort = new ChainEvent(
32 | transaction.onabort, reject).event
33 | })
34 | },
35 |
36 | /** Chain an add event. Provide the @param store and @param object and
37 | this method gives you convenient hooks into the database events.
38 |
39 | @param event_callback (within active transaction)
40 | @return Promise (resolves or rejects outside of the transaction)
41 | */
42 | add: (store, object, event_callback) => {
43 | return function(object, event_callback) {
44 | let request = store.add(object);
45 | let event_promise = null;
46 | if(event_callback)
47 | request.onsuccess = new ChainEvent(
48 | request.onsuccess, event => {
49 | event_promise = event_callback(event);
50 | }).event;
51 |
52 | let request_promise = idb_helper.on_request_end(request).then( event => {
53 | //DEBUG console.log('... object',object,'result',event.target.result,'event',event)
54 | if ( event.target.result != void 0) {
55 | //todo does event provide the keyPath name? (instead of id)
56 | object.id = event.target.result;
57 | }
58 | return [ object, event ];
59 | });
60 |
61 | if(event_promise)
62 | return Promise.all([event_promise, request_promise]);
63 | return request_promise;
64 |
65 | }(object, event_callback);//copy var references for callbacks
66 | },
67 |
68 | /** callback may return false to indicate that iteration should stop */
69 | cursor: (store_name, callback, transaction) => {
70 | return new Promise((resolve, reject)=>{
71 | if( ! transaction) {
72 | transaction = db.transaction(
73 | [store_name], "readonly"
74 | )
75 | transaction.onerror = error => {
76 | console.error("ERROR idb_helper.cursor transaction", error)
77 | reject(error)
78 | }
79 | }
80 |
81 | let store = transaction.objectStore(store_name);
82 | let request = store.openCursor()
83 | request.onsuccess = e => {
84 | let cursor = e.target.result;
85 | var ret = callback(cursor, e)
86 | if(ret === false) resolve()
87 | if(!cursor) resolve(ret)
88 | };
89 | request.onerror = (e) => {
90 | var error = {
91 | error: e.target.error.message,
92 | data: e
93 | }
94 | console.log("ERROR idb_helper.cursor request", error)
95 | reject(error);
96 | };
97 |
98 | }).then()
99 | },
100 |
101 | autoIncrement_unique: (db, table_name, unique_index) => {
102 | return db.createObjectStore(
103 | table_name, { keyPath: "id", autoIncrement: true }
104 | ).createIndex(
105 | "by_"+unique_index, unique_index, { unique: true }
106 | )
107 | }
108 |
109 | }
110 |
111 | class ChainEvent {
112 | constructor(existing_on_event, callback, request) {
113 | this.event = (event)=> {
114 | if(event.target.error)
115 | console.error("---- transaction error ---->", event.target.error)
116 | //event.request = request
117 | callback(event)
118 | if(existing_on_event)
119 | existing_on_event(event)
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/app/idb-root.js:
--------------------------------------------------------------------------------
1 | import idb_helper from "./idb-helper"
2 | import {Apis} from "bitsharesjs-ws";
3 |
4 | const DB_VERSION_MAIN = 1
5 | const DB_PREFIX = "btsgo_db"
6 |
7 | /** Usage: openIndexDB.then( db => ... */
8 | export default class iDBRoot {
9 |
10 | constructor(impl) {
11 | this.impl = impl
12 | }
13 |
14 | setDbSuffix(db_suffix) {
15 | // "graphene_db_06f667"
16 | this.database_name = DB_PREFIX + db_suffix
17 | }
18 |
19 | /** @return promise */
20 | openIndexedDB() {
21 | if(this.db) return Promise.resolve(this.db)
22 | return new Promise( (resolve, reject) => {
23 | var openRequest = this.impl.open(this.database_name, DB_VERSION_MAIN)
24 | openRequest.onupgradeneeded = e => {
25 | this.db = e.target.result
26 | this.db.createObjectStore("properties", { keyPath: "name" })
27 | }
28 | openRequest.onsuccess = e => {
29 | this.db = e.target.result
30 | resolve(this.db)
31 | }
32 | openRequest.onerror = e => {
33 | reject(e.target.error)
34 | }
35 | })
36 | }
37 |
38 | /** @return promise */
39 | getProperty(name, default_value) {
40 | return this.openIndexedDB().then( db => {
41 | var transaction = db.transaction(["properties"], "readonly")
42 | var store = transaction.objectStore("properties")
43 | return idb_helper.on_request_end( store.get(name) ).then( event => {
44 | var result = event.target.result
45 | return result ? result.value : default_value
46 | })
47 | }).catch( error => { console.error(error); throw error })
48 | }
49 |
50 | /** @return promise */
51 | setProperty(name, value) {
52 | return this.openIndexedDB().then( db => {
53 | var transaction = db.transaction(["properties"], "readwrite")
54 | var store = transaction.objectStore("properties")
55 | if(value && value["toJS"]) value = value.toJS() //Immutable-js
56 | return idb_helper.on_request_end( store.put({name, value}) )
57 | }).catch( error => { console.error(error); throw error })
58 | }
59 |
60 | deleteDatabase(are_you_sure = false) {
61 | if( ! are_you_sure) return "Are you sure?"
62 | console.log("deleting", this.database_name)
63 | var req = iDB.impl.deleteDatabase(this.database_name)
64 | return req.result
65 | }
66 |
67 | close() {
68 | this.db.close()
69 | this.db = null
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/10.
3 | */
4 |
5 | import './assets/loader';
6 | import './app';
7 |
8 |
--------------------------------------------------------------------------------
/app/stores/AccountRefsStore.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import iDB from "../idb-instance";
3 | import Immutable from "immutable";
4 | import BaseStore from "./BaseStore";
5 | import {ChainStore} from "bitsharesjs";
6 | import PrivateKeyStore from "./PrivateKeyStore"
7 | import PrivateKeyActions from "../actions/PrivateKeyActions"
8 |
9 | class AccountRefsStore extends BaseStore {
10 |
11 | constructor() {
12 | super()
13 | this._export("loadDbData")
14 | this.state = this._getInitialState()
15 | this.bindListeners({ onAddPrivateKey: PrivateKeyActions.addKey })
16 | this.no_account_refs = Immutable.Set() // Set of account ids
17 | ChainStore.subscribe(this.chainStoreUpdate.bind(this))
18 | }
19 |
20 | _getInitialState() {
21 | this.chainstore_account_ids_by_key = null
22 | return {
23 | account_refs: Immutable.Set()
24 | // loading_account_refs: false
25 | }
26 | }
27 |
28 | onAddPrivateKey({private_key_object}) {
29 | if(ChainStore.getAccountRefsOfKey(private_key_object.pubkey) !== undefined)
30 | this.chainStoreUpdate()
31 | }
32 |
33 | loadDbData() {
34 | this.setState(this._getInitialState())
35 | return loadNoAccountRefs()
36 | .then( no_account_refs => this.no_account_refs = no_account_refs )
37 | .then( ()=> this.chainStoreUpdate() )
38 | }
39 |
40 | chainStoreUpdate() {
41 | if(this.chainstore_account_ids_by_key === ChainStore.account_ids_by_key) return
42 | this.chainstore_account_ids_by_key = ChainStore.account_ids_by_key
43 | this.checkPrivateKeyStore()
44 | }
45 |
46 | checkPrivateKeyStore() {
47 | var no_account_refs = this.no_account_refs
48 | var account_refs = Immutable.Set()
49 | PrivateKeyStore.getState().keys.keySeq().forEach( pubkey => {
50 | if(no_account_refs.has(pubkey)) return
51 | var refs = ChainStore.getAccountRefsOfKey(pubkey)
52 | if(refs === undefined) return
53 | if( ! refs.size) {
54 | // Performance optimization...
55 | // There are no references for this public key, this is going
56 | // to block it. There many be many TITAN keys that do not have
57 | // accounts for example.
58 | {
59 | // Do Not block brainkey generated keys.. Those are new and
60 | // account references may be pending.
61 | var private_key_object = PrivateKeyStore.getState().keys.get(pubkey)
62 | if( typeof private_key_object.brainkey_sequence === 'number' ) {
63 | return
64 | }
65 | }
66 | no_account_refs = no_account_refs.add(pubkey)
67 | return
68 | }
69 | account_refs = account_refs.add(refs.valueSeq())
70 | })
71 | account_refs = account_refs.flatten()
72 | if( ! this.state.account_refs.equals(account_refs)) {
73 | // console.log("AccountRefsStore account_refs",account_refs.size);
74 | this.setState({account_refs})
75 | }
76 | if(!this.no_account_refs.equals(no_account_refs)) {
77 | this.no_account_refs = no_account_refs
78 | saveNoAccountRefs(no_account_refs)
79 | }
80 | }
81 |
82 | }
83 |
84 | export default alt.createStore(AccountRefsStore, "AccountRefsStore")
85 |
86 | // Performance optimization for large wallets
87 | function loadNoAccountRefs() {
88 | return iDB.root.getProperty("no_account_refs", [])
89 | .then( array => Immutable.Set(array) )
90 | }
91 |
92 | function saveNoAccountRefs(no_account_refs) {
93 | var array = []
94 | for(let pubkey of no_account_refs) array.push(pubkey)
95 | iDB.root.setProperty("no_account_refs", array)
96 | }
97 |
--------------------------------------------------------------------------------
/app/stores/AddressIndex.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import iDB from "../idb-instance";
3 | import {key} from "bitsharesjs";
4 | import {ChainConfig} from "bitsharesjs-ws";
5 | import Immutable from "immutable"
6 | import BaseStore from "./BaseStore"
7 |
8 | class AddressIndex extends BaseStore {
9 |
10 | constructor() {
11 | super()
12 | this.state = {
13 | addresses: Immutable.Map(),
14 | saving: false
15 | }
16 | this.pubkeys = new Set()
17 | // loadAddyMap is for debugging, this.add will load this on startup
18 | this._export("add", "addAll", "loadAddyMap")
19 | }
20 |
21 | saving() {
22 | if( this.state.saving ) return
23 | this.state.saving = true
24 | this.setState({ saving: true })
25 | }
26 |
27 | /** Add public key string (if not already added). Reasonably efficient
28 | for less than 10K keys.
29 | */
30 | add(pubkey) {
31 | this.loadAddyMap().then( () => {
32 | var dirty = false
33 | if(this.pubkeys.has(pubkey)) return
34 | this.pubkeys.add(pubkey)
35 | this.saving()
36 | // Gather all 5 legacy address formats (see key.addresses)
37 | var address_strings = key.addresses(pubkey)
38 | for(let address of address_strings) {
39 | this.state.addresses = this.state.addresses.set(address, pubkey)
40 | dirty = true
41 | }
42 | if( dirty ) {
43 | this.setState({ addresses: this.state.addresses })
44 | this.saveAddyMap()
45 | } else this.setState({saving: false})
46 | }).catch ( e => { throw e })
47 | }
48 |
49 | /** Worker thread implementation (for more than 10K keys) */
50 | addAll(pubkeys) {
51 | return new Promise( (resolve, reject) => {
52 | this.saving()
53 | this.loadAddyMap().then( () => {
54 | var AddressIndexWorker = require("worker?name=/[hash].js!../workers/AddressIndexWorker")
55 | var worker = new AddressIndexWorker
56 | worker.postMessage({ pubkeys, address_prefix: ChainConfig.address_prefix })
57 | var _this = this
58 | worker.onmessage = event => { try {
59 | var key_addresses = event.data
60 | var dirty = false
61 | var addresses = _this.state.addresses.withMutations( addresses => {
62 | for(let i = 0; i < pubkeys.length; i++) {
63 | var pubkey = pubkeys[i]
64 | if(_this.pubkeys.has(pubkey)) continue
65 | _this.pubkeys.add(pubkey)
66 | // Gather all 5 legacy address formats (see key.addresses)
67 | var address_strings = key_addresses[i]
68 | for(let address of address_strings) {
69 | addresses.set(address, pubkey)
70 | dirty = true
71 | }
72 | }
73 | })
74 | if( dirty ) {
75 | _this.setState({ addresses })
76 | _this.saveAddyMap()
77 | } else {
78 | _this.setState({ saving: false })
79 | }
80 | resolve()
81 | } catch( e ) { console.error('AddressIndex.addAll', e); reject(e) }}
82 | }).catch ( e => { throw e })
83 | })
84 | }
85 |
86 | loadAddyMap() {
87 | if(this.loadAddyMapPromise) return this.loadAddyMapPromise
88 | this.loadAddyMapPromise = iDB.root.getProperty("AddressIndex").then( map => {
89 | this.state.addresses = map ? Immutable.Map(map) : Immutable.Map()
90 | console.log("AddressIndex load", this.state.addresses.size)
91 | this.state.addresses.valueSeq().forEach( pubkey => this.pubkeys.add(pubkey) )
92 | this.setState({ addresses: this.state.addresses })
93 | })
94 | return this.loadAddyMapPromise
95 | }
96 |
97 | saveAddyMap() {
98 | clearTimeout(this.saveAddyMapTimeout)
99 | this.saveAddyMapTimeout = setTimeout(()=> {
100 | console.log("AddressIndex save", this.state.addresses.size)
101 | this.setState({saving: false})
102 | // If indexedDB fails to save, it will re-try via PrivateKeyStore calling this.add
103 | return iDB.root.setProperty("AddressIndex", this.state.addresses.toObject())
104 | }, 100)
105 | }
106 |
107 | }
108 | // console.log("post msg a");
109 | // worker.postMessage("a")
110 | export default alt.createStore(AddressIndex, "AddressIndex");
111 |
--------------------------------------------------------------------------------
/app/stores/AssetStore.js:
--------------------------------------------------------------------------------
1 | import BaseStore from "./BaseStore";
2 | import Immutable from "immutable";
3 | import alt from "../../common/altObj";
4 | import AssetActions from "../actions/AssetActions";
5 | import {Asset} from "./tcomb_structs";
6 | import utils from "../../common/utils";
7 |
8 |
9 | class AssetStore extends BaseStore {
10 | constructor() {
11 | super();
12 | this.assets = Immutable.Map();
13 | this.asset_symbol_to_id = {};
14 | this.searchTerms = {};
15 | this.lookupResults = [];
16 |
17 | this.bindListeners({
18 | onGetAssetList: AssetActions.getAssetList,
19 | onGetAsset: AssetActions.getAsset,
20 | onLookupAsset: AssetActions.lookupAsset
21 | });
22 | this._export("getAsset");
23 | }
24 |
25 | getAsset(id_or_symbol) {
26 | let id = utils.is_object_id(id_or_symbol) ? id_or_symbol : this.asset_symbol_to_id[id_or_symbol];
27 | return this.assets.get(id);
28 | }
29 |
30 | onGetAssetList(payload) {
31 | if (!payload) {
32 | return false;
33 | }
34 |
35 | payload.assets.forEach(asset => {
36 |
37 | for (var i = 0; i < payload.dynamic_data.length; i++) {
38 | if (payload.dynamic_data[i].id === asset.dynamic_asset_data_id) {
39 | asset.dynamic_data = payload.dynamic_data[i];
40 | break;
41 | }
42 | }
43 |
44 | if (asset.bitasset_data_id) {
45 | asset.market_asset = true;
46 |
47 | for (var i = 0; i < payload.bitasset_data.length; i++) {
48 | if (payload.bitasset_data[i].id === asset.bitasset_data_id) {
49 | asset.bitasset_data = payload.bitasset_data[i];
50 | break;
51 | }
52 | }
53 | } else {
54 | asset.market_asset = false;
55 | }
56 |
57 | this.assets = this.assets.set(
58 | asset.id,
59 | Asset(asset)
60 | );
61 |
62 | this.asset_symbol_to_id[asset.symbol] = asset.id;
63 | });
64 |
65 | }
66 |
67 | onGetAsset(payload) {
68 | let {
69 | asset
70 | } = payload;
71 |
72 | if (payload.asset === null) {
73 | this.assets = this.assets.set(
74 | payload.symbol,
75 | {notFound: true}
76 | );
77 | return true;
78 | }
79 |
80 | // console.log("onGetAsset payload:", payload);
81 | asset.dynamic_data = payload.dynamic_data;
82 |
83 | if (payload.bitasset_data) {
84 | asset.bitasset_data = payload.bitasset_data;
85 | asset.market_asset = true;
86 | } else {
87 | asset.market_asset = false;
88 | }
89 |
90 | this.assets = this.assets.set(
91 | asset.id,
92 | Asset(asset)
93 | );
94 |
95 | this.asset_symbol_to_id[asset.symbol] = asset.id;
96 | }
97 |
98 | onLookupAsset(payload) {
99 | this.searchTerms[payload.searchID] = payload.symbol;
100 | this.lookupResults = payload.assets;
101 | }
102 | }
103 |
104 | export default alt.createStore(AssetStore, "AssetStore");
--------------------------------------------------------------------------------
/app/stores/BackupStore.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import BackupActions from "../actions/BackupActions";
3 | import BaseStore from "./BaseStore";
4 | import {hash, PublicKey} from "bitsharesjs";
5 |
6 | class BackupStore extends BaseStore {
7 |
8 | constructor() {
9 | super()
10 | this.state = this._getInitialState()
11 | this.bindListeners({
12 | onIncommingFile: BackupActions.incommingWebFile,
13 | onIncommingBuffer: BackupActions.incommingBuffer,
14 | onReset: BackupActions.reset
15 | })
16 | this._export("setWalletObjct")
17 | }
18 |
19 | _getInitialState() {
20 | return {
21 | name: null,
22 | contents: null,
23 | sha1: null,
24 | size: null,
25 | last_modified: null,
26 | public_key: null,
27 | wallet_object: null
28 | }
29 | }
30 |
31 | setWalletObjct(wallet_object) {
32 | this.setState({wallet_object})
33 | }
34 |
35 | onReset() {
36 | this.setState(this._getInitialState())
37 | }
38 |
39 | onIncommingFile({name, contents, last_modified}) {
40 | var sha1 = hash.sha1(contents).toString('hex')
41 | var size = contents.length
42 | var public_key = getBackupPublicKey(contents)
43 | this.setState({ name, contents, sha1, size, last_modified, public_key })
44 | }
45 |
46 | onIncommingBuffer({name, contents, public_key}) {
47 | this.onReset()
48 | var sha1 = hash.sha1(contents).toString('hex')
49 | var size = contents.length
50 | if( ! public_key) public_key = getBackupPublicKey(contents)
51 | this.setState({name, contents, sha1, size, public_key})
52 | }
53 | }
54 |
55 | export var BackupStoreWrapped = alt.createStore(BackupStore, "BackupStore");
56 | export default BackupStoreWrapped
57 |
58 | function getBackupPublicKey(contents) {
59 | try {
60 | return PublicKey.fromBuffer(contents.slice(0, 33))
61 | } catch(e) {
62 | console.error(e, e.stack)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/stores/BaseStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/27.
3 | */
4 |
5 | export const STORAGE_KEY = "__btsgo__";
6 |
7 | class BaseStore {
8 |
9 | _export(...methods) {
10 | let publicMethods = {};
11 | methods.forEach((method) => {
12 | if(!this[method]) throw new Error(`BaseStore._export: method '${method}' not found in ${this.__proto__._storeName}`);
13 | this[method] = this[method].bind(this);
14 | publicMethods[method] = this[method];
15 | });
16 | this.exportPublicMethods(publicMethods);
17 | }
18 | }
19 |
20 | export default BaseStore;
--------------------------------------------------------------------------------
/app/stores/CachedPropertyStore.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj"
2 | import Immutable from "immutable"
3 | import iDB from "../idb-instance"
4 | import BaseStore from "./BaseStore"
5 | import CachedPropertyActions from "../actions/CachedPropertyActions"
6 |
7 | class CachedPropertyStore extends BaseStore {
8 |
9 | constructor() {
10 | super()
11 | this.state = this._getInitialState()
12 | this.bindListeners({
13 | onSet: CachedPropertyActions.set,
14 | onGet: CachedPropertyActions.get
15 | })
16 | this._export("get", "reset")
17 | }
18 |
19 | _getInitialState() {
20 | return {
21 | props: Immutable.Map()
22 | }
23 | }
24 |
25 | get(name) {
26 | return this.onGet({ name })
27 | }
28 |
29 | onSet({ name, value }) {
30 | if(this.state.props.get(name) === value) return
31 | var props = this.state.props.set(name, value)
32 | this.state.props = props
33 | iDB.setCachedProperty(name, value).then(()=>
34 | this.setState({ props }))
35 | }
36 |
37 | onGet({ name }) {
38 | var value = this.state.props.get(name)
39 | if(value !== undefined) return value
40 | iDB.getCachedProperty(name, null).then( value => {
41 | var props = this.state.props.set(name, value)
42 | this.state.props = props
43 | this.setState({ props })
44 | })
45 | }
46 |
47 | reset() {
48 | this.state = this._getInitialState()
49 | this.setState(this.state)
50 | }
51 | }
52 |
53 | export var CachedPropertyStoreWrapped = alt.createStore(CachedPropertyStore, "CachedPropertyStore")
54 | export default CachedPropertyStoreWrapped
--------------------------------------------------------------------------------
/app/stores/ImportKeysStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/1/27.
3 | */
4 |
5 | import alt from "../../common/altObj";
6 | import BaseStore from "./BaseStore";
7 |
8 | class ImportKeysStore extends BaseStore {
9 |
10 | constructor() {
11 | super()
12 | this.state = this._getInitialState()
13 | this._export("importing")
14 | }
15 |
16 | _getInitialState() {
17 | return { importing: false }
18 | }
19 |
20 | importing(importing) {
21 | this.setState({ importing })
22 | }
23 | }
24 |
25 | export default alt.createStore(ImportKeysStore, "ImportKeysStore");
--------------------------------------------------------------------------------
/app/stores/IntlStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2016/12/27.
3 | */
4 |
5 | import alt from '../../common/altObj';
6 | import BaseStore, {STORAGE_KEY} from './BaseStore';
7 | import IntlActions from '../actions/IntlActions';
8 | import SettingsActions from '../actions/SettingsActions';
9 | import ls from '../../common/localStorage';
10 |
11 | let ss = new ls(STORAGE_KEY);
12 |
13 | import {addLocaleData} from 'react-intl';
14 | import {zh_CN} from '../assets/locales/locale-zh';
15 | import {en_US} from '../assets/locales/locale-en';
16 | import en from 'react-intl/locale-data/en';
17 | import zh from 'react-intl/locale-data/zh';
18 | addLocaleData(zh);
19 | addLocaleData(en);
20 |
21 | class IntlStore extends BaseStore {
22 | constructor() {
23 | super();
24 | this.currentLocale = ss.has("settings_v3") ? ss.get("settings_v3").locale : "zh";
25 |
26 | this.locales = ["zh", "en"];
27 | //console.debug(zh_CN);
28 | this.localesObject = {zh: zh_CN, en: en_US};
29 |
30 | this.bindListeners({
31 | onSwitchLocale: IntlActions.switchLocale,
32 | onGetLocale: IntlActions.getLocale,
33 | onClearSettings: SettingsActions.clearSettings
34 | });
35 |
36 | this._export("getCurrentLocale", "hasLocale");
37 | }
38 |
39 | hasLocale(locale) {
40 | return this.locales.indexOf(locale) !== -1;
41 | }
42 |
43 | getCurrentLocale() {
44 | return this.currentLocale;
45 | }
46 |
47 | onSwitchLocale({locale}) {
48 |
49 | switch (locale) {
50 | case "zh":
51 | this.localesObject[locale] = zh_CN;
52 | break;
53 | case "en":
54 | this.localesObject[locale] = en_US;
55 | break;
56 | default:
57 | this.localesObject[locale] = zh_CN;
58 | break;
59 | }
60 | this.currentLocale = locale;
61 | }
62 |
63 | onGetLocale(locale) {
64 | if (this.locales.indexOf(locale) === -1) {
65 | this.locales.push(locale);
66 | }
67 | }
68 |
69 | onClearSettings() {
70 | this.onSwitchLocale("zh");
71 | }
72 | }
73 |
74 | export default alt.createStore(IntlStore, "IntlStore");
--------------------------------------------------------------------------------
/app/stores/NotificationStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/1/27.
3 | */
4 |
5 | import alt from "../../common/altObj";
6 | import NotificationActions from "../actions/NotificationActions";
7 |
8 |
9 | class NotificationStore {
10 |
11 | constructor() {
12 | this.bindListeners({
13 | addNotification: [
14 | NotificationActions.addNotification,
15 | NotificationActions.success,
16 | NotificationActions.warning,
17 | NotificationActions.error,
18 | NotificationActions.info
19 | ]
20 | })
21 |
22 | this.state = {
23 | notification: null
24 | }
25 | }
26 |
27 | addNotification(notification) {
28 | this.setState({ notification: notification })
29 | }
30 | }
31 |
32 | export default alt.createStore(NotificationStore, 'NotificationStore');
--------------------------------------------------------------------------------
/app/stores/ScanStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2017/10/18.
3 | */
4 |
5 | import alt from '../../common/altObj';
6 | import BaseStore from "./BaseStore";
7 | import ScanActions from "../actions/ScanActions";
8 |
9 | class ScanStore extends BaseStore {
10 | constructor() {
11 | super();
12 | this.bindActions(ScanActions);
13 | this.state = this.__initState();
14 | }
15 |
16 | __initState() {
17 | return {routerState: null, qrStr: null};
18 | }
19 |
20 | onScan(routerState) {
21 | this.setState({routerState});
22 | }
23 |
24 | onSetScanResult(qrStr) {
25 | this.setState({qrStr});
26 | }
27 |
28 | onReset() {
29 | this.setState(this.__initState());
30 | }
31 | }
32 |
33 | export default alt.createStore(ScanStore, "ScanStore");
--------------------------------------------------------------------------------
/app/stores/TransactionConfirmStore.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import TransactionConfirmActions from "../actions/TransactionConfirmActions";
3 |
4 | class TransactionConfirmStore {
5 |
6 | constructor() {
7 | this.bindActions(TransactionConfirmActions);
8 | this.state = this.getInitialState();
9 | this.exportPublicMethods({reset: this.reset.bind(this)});
10 | }
11 |
12 | getInitialState() {
13 | //console.log("-- TransactionConfirmStore.getInitialState -->");
14 | return {
15 | transaction: null,
16 | error: null,
17 | broadcasting: false,
18 | broadcast: false,
19 | included: false,
20 | trx_id: null,
21 | trx_block_num: null,
22 | closed: true,
23 | broadcasted_transaction: null,
24 | propose: false,
25 | fee_paying_account: null // proposal fee_paying_account
26 | };
27 | }
28 |
29 | onConfirm({transaction}) {
30 | let init_state = this.getInitialState();
31 | let state = {...init_state, transaction: transaction, closed: false, broadcasted_transaction: null}
32 | //console.log("-- TransactionConfirmStore.onConfirm -->", state);
33 | this.setState(state);
34 | }
35 |
36 | onClose() {
37 | //console.log("-- TransactionConfirmStore.onClose -->", state);
38 | this.setState({closed: true});
39 | }
40 |
41 | onBroadcast(payload) {
42 | //console.log("-- TransactionConfirmStore.onBroadcast -->", state);
43 | this.setState(payload);
44 | if (payload.broadcasted_transaction) {
45 | this.setState({
46 | broadcasted_transaction: this.state.transaction
47 | });
48 | }
49 | }
50 |
51 | onWasBroadcast(res) {
52 | //console.log("-- TransactionConfirmStore.onWasBroadcast -->", state);
53 | this.setState({broadcasting: false, broadcast: true});
54 | }
55 |
56 | onWasIncluded(res) {
57 | //console.log("-- TransactionConfirmStore.onWasIncluded -->", this.state);
58 | this.setState({
59 | error: null,
60 | broadcasting: false,
61 | broadcast: true,
62 | included: true,
63 | trx_id: res[0].id,
64 | trx_block_num: res[0].block_num,
65 | broadcasted_transaction: this.state.transaction
66 | });
67 | }
68 |
69 | onError({ error }) {
70 | this.setState({broadcast: false, broadcasting: false, error});
71 | }
72 |
73 | onTogglePropose() {
74 | this.setState({ propose: ! this.state.propose });
75 | }
76 |
77 | onProposeFeePayingAccount(fee_paying_account) {
78 | this.setState({ fee_paying_account });
79 | }
80 |
81 | reset() {
82 | //console.log("-- TransactionConfirmStore.reset -->");
83 | this.state = this.getInitialState();
84 | }
85 |
86 | }
87 |
88 | export default alt.createStore(TransactionConfirmStore, 'TransactionConfirmStore');
89 |
--------------------------------------------------------------------------------
/app/stores/WalletUnlockStore.js:
--------------------------------------------------------------------------------
1 | import alt from "../../common/altObj";
2 | import BaseStore, {STORAGE_KEY} from "./BaseStore";
3 |
4 | import WalletUnlockActions from "../actions/WalletUnlockActions";
5 | import SettingsActions from "../actions/SettingsActions";
6 | import WalletDb from "./WalletDb";
7 | import ls from "../../common/localStorage";
8 |
9 | let ss = new ls(STORAGE_KEY);
10 |
11 | class WalletUnlockStore extends BaseStore {
12 |
13 | constructor() {
14 | super();
15 | this.bindActions(WalletUnlockActions);
16 | this.state = {locked: true};
17 |
18 | this.walletLockTimeout = this._getTimeout(); // seconds (10 minutes
19 | this.timeout = null;
20 |
21 | this.bindListeners({
22 | onChangeSetting: SettingsActions.changeSetting
23 | });
24 |
25 | // let timeoutSetting = this._getTimeout();
26 |
27 | // if (timeoutSetting) {
28 | // this.walletLockTimeout = timeoutSetting;
29 | // }
30 |
31 | }
32 |
33 | onUnlock({resolve, reject}) {
34 | console.log('... onUnlock setState', WalletDb.isLocked());
35 |
36 | this._setLockTimeout();
37 | if (!WalletDb.isLocked()) {
38 | resolve()
39 | return
40 | }
41 |
42 | this.setState({resolve, reject, locked: WalletDb.isLocked()});
43 | }
44 |
45 | onLock({resolve}) {
46 | //DEBUG console.log('... WalletUnlockStore\tprogramatic lock', WalletDb.isLocked())
47 | if (WalletDb.isLocked()) {
48 | resolve()
49 | return
50 | }
51 | WalletDb.onLock()
52 | this.setState({resolve: null, reject: null, locked: WalletDb.isLocked()})
53 | resolve()
54 | }
55 |
56 | onCancel() {
57 | //this.state.reject();
58 | this.setState({resolve: null, reject: null});
59 | }
60 |
61 | onChange() {
62 | this.setState({locked: WalletDb.isLocked()})
63 | }
64 |
65 |
66 | onChangeSetting(payload) {
67 | if (payload.setting === "walletLockTimeout") {
68 | this.walletLockTimeout = payload.value;
69 | this._clearLockTimeout();
70 | this._setLockTimeout();
71 | }
72 | }
73 |
74 |
75 | _setLockTimeout() {
76 | this._clearLockTimeout();
77 | this.timeout = setTimeout(() => {
78 | if (!WalletDb.isLocked()) {
79 | console.log("auto locking after", this.walletLockTimeout, "s");
80 | WalletDb.onLock()
81 | this.setState({locked: true})
82 | }
83 | ;
84 | }, this.walletLockTimeout * 1000);
85 | }
86 |
87 | _clearLockTimeout() {
88 | if (this.timeout) {
89 | clearTimeout(this.timeout);
90 | this.timeout = null;
91 | }
92 | }
93 |
94 | _getTimeout() {
95 | return parseInt(ss.get("lockTimeout", 600), 10);
96 | }
97 | }
98 |
99 | export default alt.createStore(WalletUnlockStore, 'WalletUnlockStore')
100 |
--------------------------------------------------------------------------------
/app/stores/layout/ConfirmStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by necklace on 2017/3/17.
3 | */
4 | import alt from "../../../common/altObj";
5 | import ConfirmActions from "../../actions/layout/ConfirmActions";
6 |
7 | class ConfirmStore {
8 | constructor() {
9 | this.bindActions(ConfirmActions);
10 | this.state = this.initState();
11 | }
12 |
13 | initState() {
14 | return {
15 | show: false,
16 | title: '',
17 | msg: '',
18 | height: 4,
19 | showCancelButton: true,
20 | onOK: null,
21 | onCancel: null
22 | };
23 | }
24 |
25 | onReset() {
26 | this.setState(this.initState());
27 | }
28 |
29 | onShow({title, msg, onok, oncancel, height, nocancel}) {
30 | this.setState(
31 | {
32 | show: true,
33 | title: title,
34 | msg: msg,
35 | height: height ? height : 4,
36 | showCancelButton: nocancel ? false : true,
37 | onOK: onok ? onok : null,
38 | onCancel: oncancel ? oncancel : null
39 | }
40 | );
41 | }
42 | }
43 | export default alt.createStore(ConfirmStore, 'ConfirmStore');
--------------------------------------------------------------------------------
/app/stores/tcomb_structs.js:
--------------------------------------------------------------------------------
1 | import t from "tcomb";
2 |
3 | let Asset = t.struct({
4 | bitasset_data_id: t.maybe(t.Str),
5 | bitasset_data: t.maybe(t.Obj),
6 | dynamic_asset_data_id: t.Str,
7 | dynamic_data: t.maybe(t.Obj),
8 | id: t.Str,
9 | issuer: t.Str,
10 | market_asset: t.Bool,
11 | options: t.Obj,
12 | precision: t.Num,
13 | symbol: t.Str
14 | }, "Asset");
15 |
16 | let Block = t.struct({
17 | extensions: t.Arr,
18 | id: t.Num,
19 | previous: t.Str,
20 | timestamp: t.Dat,
21 | transactions: t.Arr,
22 | transaction_merkle_root: t.Str,
23 | witness: t.Str,
24 | witness_signature: t.Str
25 | }, "Block");
26 |
27 | let WalletTcomb = t.struct({
28 | public_name: t.Str,
29 | created: t.Dat,
30 | last_modified: t.Dat,
31 | backup_date: t.maybe(t.Dat),
32 | password_pubkey: t.Str,
33 | encryption_key: t.Str,
34 | encrypted_brainkey: t.maybe(t.Str),
35 | brainkey_pubkey: t.Str,
36 | brainkey_sequence: t.Num,
37 | brainkey_backup_date: t.maybe(t.Dat),
38 | deposit_keys: t.maybe(t.Obj),
39 | // password_checksum: t.Str,
40 | chain_id: t.Str
41 | }, "WalletTcomb");
42 |
43 | let PrivateKeyTcomb = t.struct({
44 | id: t.maybe(t.Num),
45 | pubkey: t.Str,
46 | label: t.maybe(t.Str),
47 | import_account_names: t.maybe(t.Arr),
48 | brainkey_sequence: t.maybe(t.Num),
49 | encrypted_key: t.Str
50 | }, "PrivateKeyTcomb");
51 |
52 | //let PublicKeyTcomb = t.struct({
53 | // id: t.maybe(t.Num),
54 | // pubkey: t.Str,
55 | // key_id: t.maybe(t.Str)
56 | //}, "PublicKeyTcomb");
57 |
58 | let LimitOrder = t.struct({
59 | expiration: t.Dat,
60 | for_sale: t.Num,
61 | id: t.Str,
62 | sell_price: t.Obj,
63 | seller: t.Str
64 | }, "LimitOrder");
65 |
66 | let SettleOrder = t.struct({
67 | settlement_date: t.Dat,
68 | balance: t.Obj,
69 | owner: t.Str
70 | }, "SettleOrder");
71 |
72 | let ShortOrder = t.struct({
73 | expiration: t.Dat,
74 | for_sale: t.Num,
75 | id: t.Str,
76 | sell_price: t.Obj,
77 | seller: t.Str
78 | }, "ShortOrder");
79 |
80 | let CallOrder = t.struct({
81 | borrower: t.Str,
82 | call_price: t.Obj,
83 | collateral: t.Num,
84 | debt: t.Num,
85 | id: t.Str
86 | }, "CallOrder");
87 |
88 |
89 | module.exports = {
90 | Asset: Asset,
91 | Block: Block,
92 | WalletTcomb: WalletTcomb,
93 | //PublicKeyTcomb: PublicKeyTcomb,
94 | PrivateKeyTcomb: PrivateKeyTcomb,
95 | LimitOrder: LimitOrder,
96 | ShortOrder: ShortOrder,
97 | CallOrder: CallOrder,
98 | SettleOrder: SettleOrder
99 | };
100 |
--------------------------------------------------------------------------------
/app/workers/AddressIndexWorker.js:
--------------------------------------------------------------------------------
1 | import {key} from "bitsharesjs";
2 |
3 | onmessage = function(event) {
4 | try {
5 | console.log("AddressIndexWorker start");
6 | var {pubkeys, address_prefix} = event.data
7 | var results = []
8 | for (let pubkey of pubkeys) {
9 | results.push( key.addresses(pubkey, address_prefix) )
10 | }
11 | postMessage( results )
12 | console.log("AddressIndexWorker done");
13 | } catch( e ) {
14 | console.error("AddressIndexWorker", e)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/workers/AesWorker.js:
--------------------------------------------------------------------------------
1 | import "babel-polyfill";
2 | import {key, Aes} from "bitsharesjs";
3 |
4 | onmessage = function(event) { try {
5 | console.log("AesWorker start");
6 | var {private_plainhex_array, iv, key} = event.data
7 | var aes = new Aes(iv, key)
8 | var private_cipherhex_array = []
9 | for(let private_plainhex of private_plainhex_array) {
10 | var private_cipherhex = aes.encryptHex( private_plainhex )
11 | private_cipherhex_array.push( private_cipherhex )
12 | }
13 | postMessage( private_cipherhex_array )
14 | console.log("AesWorker done");
15 | } catch( e ) { console.error("AesWorker", e) } }
16 |
--------------------------------------------------------------------------------
/app/workers/GenesisFilterWorker.js:
--------------------------------------------------------------------------------
1 |
2 | import GenesisFilter from "../../common/GenesisFilter";
3 |
4 | onmessage = function(event) { try {
5 | console.log("GenesisFilterWorker start");
6 | var { account_keys, bloom_filter } = event.data
7 | var genesis_filter = new GenesisFilter( bloom_filter )
8 | genesis_filter.filter( account_keys, status => {
9 | if( status.success ) {
10 | postMessage({ account_keys, status })
11 | console.log("GenesisFilterWorker done")
12 | return
13 | }
14 | postMessage({ status })
15 | })
16 | } catch( e ) { console.error("GenesisFilterWorker", e) } }
--------------------------------------------------------------------------------
/build/.gitignore:
--------------------------------------------------------------------------------
1 | assets
--------------------------------------------------------------------------------
/build/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/build/favicon.ico
--------------------------------------------------------------------------------
/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | BTSGO.NET-分布式交易系统
10 |
11 |
26 |
35 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/common/account_constants.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | account_listing: {
3 | no_listing: 0x0, ///< No opinion is specified about this account
4 | white_listed: 0x1, ///< This account is whitelisted, but not blacklisted
5 | black_listed: 0x2, ///< This account is blacklisted, but not whitelisted
6 | white_and_black_listed: 0x1 | 0x2 ///< This account is both whitelisted and blacklisted
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/common/account_utils.js:
--------------------------------------------------------------------------------
1 | import {ChainStore} from "bitsharesjs";
2 | import utils from "./utils";
3 | import {Asset} from "./MarketClasses";
4 |
5 | export default class AccountUtils {
6 |
7 | /**
8 | * takes asset as immutable object or id, fee as integer amount
9 | * @return undefined if asset is undefined
10 | * @return false if fee pool has insufficient balance
11 | * @return true if the fee pool has sufficient balance
12 | */
13 | static checkFeePool(asset, fee) {
14 | asset = asset.toJS ? asset : ChainStore.getAsset(asset);
15 | if (!asset) {
16 | return undefined;
17 | }
18 |
19 | let feePool = parseInt(asset.getIn(["dynamic", "fee_pool"]), 10);
20 |
21 | return feePool >= fee;
22 | }
23 |
24 | static getPossibleFees(account, operation) {
25 | let core = ChainStore.getAsset("1.3.0");
26 | account = !account || account.toJS ? account : ChainStore.getAccount(account);
27 |
28 | if (!account || !core) {
29 | return {assets: ["1.3.0"], fees: {"1.3.0": 0}};
30 | }
31 |
32 | let assets = [], fees = {};
33 |
34 | let globalObject = ChainStore.getObject("2.0.0");
35 |
36 | let fee = utils.estimateFee(operation, null, globalObject);
37 |
38 | let accountBalances = account.get("balances");
39 | if (!accountBalances) {
40 | return {assets: ["1.3.0"], fees: {"1.3.0": 0}};
41 | }
42 |
43 | accountBalances.forEach((balanceID, assetID) => {
44 | let balanceObject = ChainStore.getObject(balanceID);
45 | let balance = balanceObject ? parseInt(balanceObject.get("balance"), 10) : 0;
46 | let hasBalance = false, eqFee;
47 |
48 | if (assetID === "1.3.0" && balance >= fee) {
49 | hasBalance = true;
50 | } else if (balance && ChainStore.getAsset(assetID)) {
51 | let asset = ChainStore.getAsset(assetID);
52 | let price = utils.convertPrice(core, asset.getIn(["options", "core_exchange_rate"]).toJS(), null, asset.get("id"));
53 |
54 | eqFee = parseInt(utils.convertValue(price, fee, core, asset), 10);
55 | if (parseInt(eqFee, 10) !== eqFee) {
56 | eqFee += 1; // Add 1 to round up;
57 | }
58 | if (balance >= eqFee && this.checkFeePool(asset, eqFee)) {
59 | hasBalance = true;
60 | }
61 | }
62 | if (hasBalance) {
63 | assets.push(assetID);
64 | fees[assetID] = eqFee ? eqFee : fee;
65 | }
66 | })
67 |
68 | return {assets, fees};
69 | }
70 |
71 | static getFinalFeeAsset(account, operation, fee_asset_id = "1.3.0") {
72 | let {assets: feeAssets} = this.getPossibleFees(account, operation);
73 | if (feeAssets.length === 1) {
74 | fee_asset_id = feeAssets[0];
75 | } else if (feeAssets.length > 0 && feeAssets.indexOf(fee_asset_id) === -1) {
76 | fee_asset_id = feeAssets[0];
77 | }
78 |
79 | return fee_asset_id;
80 | }
81 |
82 | static getBalanceById(balanceId) {
83 | if (balanceId) {
84 | let balance = ChainStore.getObject(balanceId);
85 | if (balance) {
86 | let asset = ChainStore.getObject(balance.get("asset_type"));
87 | let amount = Number(balance.get("balance"));
88 | //console.debug(balance.get("asset_type"))
89 | //console.debug(asset.get('precision'))
90 | return new Asset({asset_id: balance.get("asset_type"), amount: amount, precision: asset.get('precision')});
91 | }
92 | }
93 | return 0;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/common/altObj.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/12.
3 | */
4 | import Alt from "alt";
5 | var alt = new Alt();
6 |
7 | // 输出所有action日志
8 | //alt.dispatcher.register(console.log.bind(console, 'alt.dispatcher'))
9 |
10 | export default alt;
--------------------------------------------------------------------------------
/common/asset_constants.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | permission_flags: {
3 | charge_market_fee : 0x01, /**< an issuer-specified percentage of all market trades in this asset is paid to the issuer */
4 | white_list : 0x02, /**< accounts must be whitelisted in order to hold this asset */
5 | override_authority : 0x04, /**< issuer may transfer asset back to himself */
6 | transfer_restricted : 0x08, /**< require the issuer to be one party to every transfer */
7 | disable_force_settle : 0x10, /**< disable force settling */
8 | global_settle : 0x20, /**< allow the bitasset issuer to force a global settling -- this may be set in permissions, but not flags */
9 | disable_confidential : 0x40, /**< allow the asset to be used with confidential transactions */
10 | witness_fed_asset : 0x80, /**< allow the asset to be fed by witnesses */
11 | committee_fed_asset : 0x100 /**< allow the asset to be fed by the committee */
12 | },
13 | uia_permission_mask: [
14 | "charge_market_fee",
15 | "white_list",
16 | "override_authority",
17 | "transfer_restricted",
18 | "disable_confidential"
19 | ],
20 | GRAPHENE_100_PERCENT: 10000,
21 | GRAPHENE_1_PERCENT: 10000 / 100
22 | };
23 |
24 |
25 | /*
26 |
27 | const static uint32_t ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_force_settle|global_settle|disable_confidential
28 | |witness_fed_asset|committee_fed_asset;
29 | const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential;
30 |
31 | */
32 |
--------------------------------------------------------------------------------
/common/asset_utils.js:
--------------------------------------------------------------------------------
1 | import assetConstants from "./asset_constants";
2 |
3 | export default class AssetUtils {
4 |
5 | static getFlagBooleans(mask, isBitAsset = false) {
6 | let booleans = {
7 | charge_market_fee : false,
8 | white_list : false,
9 | override_authority : false,
10 | transfer_restricted : false,
11 | disable_force_settle : false,
12 | global_settle : false,
13 | disable_confidential : false,
14 | witness_fed_asset : false,
15 | committee_fed_asset : false
16 | }
17 |
18 | if (mask === "all") {
19 | for (let flag in booleans) {
20 | if (!isBitAsset && (assetConstants.uia_permission_mask.indexOf(flag) === -1)) {
21 | delete booleans[flag];
22 | } else {
23 | booleans[flag] = true;
24 | }
25 | }
26 | return booleans;
27 | }
28 |
29 | for (let flag in booleans) {
30 | if (!isBitAsset && (assetConstants.uia_permission_mask.indexOf(flag) === -1)) {
31 | delete booleans[flag];
32 | } else {
33 | if (mask & assetConstants.permission_flags[flag]) {
34 | booleans[flag] = true;
35 | }
36 | }
37 | }
38 |
39 | return booleans;
40 | }
41 |
42 | static getFlags(flagBooleans) {
43 | let keys = Object.keys(assetConstants.permission_flags);
44 |
45 | let flags = 0;
46 |
47 | keys.forEach(key => {
48 | if (flagBooleans[key] && key !== "global_settle") {
49 | flags += assetConstants.permission_flags[key];
50 | }
51 | })
52 |
53 | return flags;
54 | }
55 |
56 | static getPermissions(flagBooleans, isBitAsset = false) {
57 | let permissions = isBitAsset ? Object.keys(assetConstants.permission_flags) : assetConstants.uia_permission_mask;
58 | let flags = 0;
59 | permissions.forEach(permission => {
60 | if (flagBooleans[permission] && permission !== "global_settle") {
61 | flags += assetConstants.permission_flags[permission];
62 | }
63 | })
64 |
65 | if (isBitAsset) {
66 | flags += assetConstants.permission_flags["global_settle"];
67 | }
68 |
69 | return flags;
70 | }
71 |
72 | static parseDescription(description) {
73 | let parsed;
74 | try {
75 | parsed = JSON.parse(description)
76 | } catch (error) {
77 |
78 | }
79 |
80 | return parsed ? parsed : {main: description};
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/common/localStorage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/11.
3 | */
4 | import ls, {ls_key_exists} from './localStorageImpl';
5 |
6 | if (null === ls) throw "localStorage is required but isn't available on this platform";
7 |
8 | module.exports = (key) => {
9 | var STORAGE_KEY = key;
10 | return {
11 | get(key, dv = {}) {
12 | let rv;
13 | if (ls_key_exists(STORAGE_KEY + key, ls)) {
14 | rv = JSON.parse(ls.getItem(STORAGE_KEY + key));
15 | }
16 | return rv ? rv : dv;
17 | },
18 | set(key, object) {
19 | if (object && object.toJS) {
20 | object = object.toJS();
21 | }
22 | ls.setItem(STORAGE_KEY + key, JSON.stringify(object));
23 | },
24 | remove(key) {
25 | ls.removeItem(STORAGE_KEY + key);
26 | },
27 | has(key) {
28 | return ls_key_exists(STORAGE_KEY + key, ls);
29 | }
30 | };
31 | }
--------------------------------------------------------------------------------
/common/localStorageImpl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/11.
3 | */
4 | var ls_key_exists = function _ls_key_exists(key,ls) { return (key in ls); }
5 | export {ls_key_exists};
6 | export default (typeof localStorage === "undefined" ? null : localStorage);
--------------------------------------------------------------------------------
/cordova/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | BTSGO
4 |
5 | BTSGO.NET-分布式交易系统
6 |
7 |
8 | Necklace
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/cordova/release.bat:
--------------------------------------------------------------------------------
1 | cordova build android --release
--------------------------------------------------------------------------------
/cordova/res/icon/android/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/icon/android/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/icon/android/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/drawable-mdpi/bts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/icon/android/drawable-mdpi/bts.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/icon/android/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/icon/android/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-land-hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-land-hdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-land-ldpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-land-ldpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-land-mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-land-mdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-land-xhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-land-xhdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-port-hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-port-hdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-port-ldpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-port-ldpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-port-mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-port-mdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/splash-port-xhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiangxn/btsgo/4f991511c371aaadb6556179e70611ebf7e6d579/cordova/res/screen/android/splash-port-xhdpi.png
--------------------------------------------------------------------------------
/cordova/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | BTSGO.NET-分布式交易系统
11 |
12 |
13 |
28 |
37 |
38 |
39 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "btsgo.net",
3 | "version": "0.1.0",
4 | "description": "bitshares2.0 UI",
5 | "main": "main.js",
6 | "devDependencies": {
7 | "babel-cli": "^6.18.0",
8 | "babel-core": "^6.20.0",
9 | "babel-loader": "^6.2.9",
10 | "babel-preset-es2015": "^6.18.0",
11 | "babel-preset-react": "^6.16.0",
12 | "babel-preset-stage-0": "^6.16.0",
13 | "css-loader": "^0.26.1",
14 | "extract-text-webpack-plugin": "^1.0.1",
15 | "file-loader": "^0.9.0",
16 | "git-rev-sync": "^1.8.0",
17 | "json-loader": "^0.5.4",
18 | "jsx-loader": "^0.13.2",
19 | "node-sass": "^4.0.0",
20 | "postcss-loader": "^1.2.2",
21 | "redux-devtools": "^3.3.1",
22 | "sass-loader": "^4.0.2",
23 | "style-loader": "^0.13.1",
24 | "url-loader": "^0.5.7",
25 | "autoprefixer": "^6.5.4",
26 | "webpack": "^1.14.0",
27 | "webpack-dev-server": "^3.1.11",
28 | "worker-loader": "^0.7.1"
29 | },
30 | "scripts": {
31 | "build": "webpack",
32 | "start": "webpack-dev-server --history-api-fallback --devtool eval --progress --colors --content-base build",
33 | "release": "webpack --config webpack.pro.config.js -p --progress --display-error-details --colors"
34 | },
35 | "repository": {
36 | "type": "git",
37 | "url": "git+https://github.com/xiangxn/btsgo.git"
38 | },
39 | "keywords": [
40 | "bts",
41 | "ui"
42 | ],
43 | "author": "necklace",
44 | "license": "MIT",
45 | "bugs": {
46 | "url": "https://github.com/xiangxn/btsgo/issues"
47 | },
48 | "homepage": "https://github.com/xiangxn/btsgo#readme",
49 | "dependencies": {
50 | "alt": "^0.18.6",
51 | "alt-container": "^1.0.2",
52 | "alt-utils": "^1.0.0",
53 | "babel-polyfill": "^6.23.0",
54 | "bignumber.js": "^4.0.0",
55 | "bitsharesjs": "^1.3.1",
56 | "exif-js": "^2.1.1",
57 | "file-saver": "^1.3.3",
58 | "fractional": "^1.0.0",
59 | "history": "^3.2.1",
60 | "immutable": "^3.8.1",
61 | "indexeddbshim": "^2.2.1",
62 | "intl": "^1.2.5",
63 | "jdenticon": "git+https://github.com/cryptonomex/jdenticon.git",
64 | "js-sha256": "^0.2.3",
65 | "lodash": "^4.17.11",
66 | "lzma": "2.1.6",
67 | "numeral": "^2.0.4",
68 | "pica": "^2.0.8",
69 | "qrcode-reader": "^0.2.2",
70 | "react": "^15.4.1",
71 | "react-dom": "^15.4.1",
72 | "react-gestures": "^0.1.8",
73 | "react-intl": "^2.2.0",
74 | "react-notification-system": "^0.2.11",
75 | "react-redux": "^4.4.6",
76 | "react-router": "^3.0.0",
77 | "redux": "^3.6.0",
78 | "tcomb": "^3.2.16",
79 | "whatwg-fetch": "^2.0.2"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/11.
3 | */
4 | let path = require('path');
5 | let webpack = require('webpack');
6 | let ExtractTextPlugin = require('extract-text-webpack-plugin');
7 | var git = require("git-rev-sync");
8 |
9 | var root_dir = path.resolve(__dirname);
10 |
11 | module.exports = {
12 | entry: ['webpack/hot/dev-server', path.resolve(root_dir, './app/main.js')],
13 | output: {
14 | path: path.resolve(root_dir, 'build/assets'),
15 | filename: 'bundle.js',
16 | publicPath: '/assets/'
17 | },
18 | devServer: {
19 | host: "0.0.0.0",
20 | port: 8080,
21 | hot: true
22 | },
23 | module: {
24 | loaders: [
25 | {
26 | test: /\.js|jsx$/,
27 | exclude: [/node_modules/],
28 | loaders: ['babel?presets[]=es2015,presets[]=react,presets[]=stage-0']
29 | },
30 | {
31 | test: /\.scss$/,
32 | loader: ExtractTextPlugin.extract('style', 'css!postcss!sass')
33 | },
34 | {
35 | test: /\.(png|jpg|jpeg|gif|woff|otf)$/, loader: 'url?limit=8192'
36 | },
37 | {
38 | test: /\.json$/, loader: 'json',
39 | exclude: [
40 | path.resolve(root_dir, "common")
41 | ]
42 | }]
43 | },
44 | postcss: [
45 | require('autoprefixer')
46 | ],
47 | plugins: [
48 | new webpack.DefinePlugin({
49 | 'process.env': {
50 | NODE_ENV: JSON.stringify('development')
51 | },
52 | APP_VERSION: JSON.stringify(git.tag()),
53 | __BASE_URL__: JSON.stringify("assets"),
54 | __HASHHISTORY__: true
55 | }),
56 | new webpack.HotModuleReplacementPlugin(),
57 | new ExtractTextPlugin('style.css', {allChunks: true})
58 | ],
59 | node: {
60 | fs: "empty"
61 | }
62 | };
--------------------------------------------------------------------------------
/webpack.pro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiangxn on 2016/12/10.
3 | */
4 | let path = require('path');
5 | let webpack = require('webpack');
6 | let ExtractTextPlugin = require('extract-text-webpack-plugin');
7 | var git = require("git-rev-sync");
8 |
9 | var root_dir = path.resolve(__dirname);
10 |
11 | module.exports = {
12 | entry: [path.resolve(root_dir, './app/main.js')],//'babel-polyfill'
13 | output: {
14 | path: path.resolve(root_dir, 'build/assets'),
15 | filename: 'bundle.js',
16 | publicPath: '/assets/'
17 | },
18 | module: {
19 | loaders: [
20 | {
21 | test: /\.js|jsx$/,
22 | exclude: [/node_modules/],
23 | loaders: ['babel?presets[]=es2015,presets[]=react,presets[]=stage-0']
24 | },
25 | {
26 | test: /\.scss$/,
27 | loader: ExtractTextPlugin.extract('style', 'css!postcss!sass')
28 | //loader: 'css!sass?sourceMap'
29 | },
30 | {
31 | test: /\.(png|jpg|jpeg|gif|woff|otf)$/, loader: 'url?limit=8192'
32 | },
33 | {
34 | test: /\.json$/, loader: 'json',
35 | exclude: [
36 | path.resolve(root_dir, "common")
37 | ]
38 | }]
39 | },
40 | postcss: [
41 | require('autoprefixer')
42 | ],
43 | plugins: [
44 | new webpack.DefinePlugin({
45 | 'process.env': {
46 | NODE_ENV: JSON.stringify('production')
47 | },
48 | APP_VERSION: JSON.stringify(git.tag()),
49 | __BASE_URL__: JSON.stringify("assets"),
50 | __HASHHISTORY__: true
51 | }),
52 | new webpack.optimize.UglifyJsPlugin({
53 | output: {comments: false},
54 | compress: {warnings: false}
55 | }),
56 | new ExtractTextPlugin('style.css', {allChunks: true})
57 | ]
58 | };
--------------------------------------------------------------------------------