From c8126ab21792d7a85e1123a2363af285a0263654 Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Fri, 10 May 2019 01:02:03 -0400 Subject: [PATCH] store user supports --- dist/bundle.es.js | 438 +++++++++++++++++++-------------- dist/flow-typed/Lbry.js | 3 + dist/flow-typed/Transaction.js | 16 ++ flow-typed/Lbry.js | 3 + flow-typed/Transaction.js | 16 ++ src/constants/action_types.js | 4 + src/index.js | 1 + src/lbry.js | 1 + src/redux/actions/claims.js | 55 +++-- src/redux/actions/wallet.js | 18 ++ src/redux/reducers/wallet.js | 403 ++++++++++++++++-------------- src/redux/selectors/wallet.js | 7 +- 12 files changed, 574 insertions(+), 391 deletions(-) diff --git a/dist/bundle.es.js b/dist/bundle.es.js index 172c991..57f1639 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -43,6 +43,10 @@ const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED'; const GET_NEW_ADDRESS_COMPLETED = 'GET_NEW_ADDRESS_COMPLETED'; const FETCH_TRANSACTIONS_STARTED = 'FETCH_TRANSACTIONS_STARTED'; const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED'; +const FETCH_SUPPORTS_STARTED = 'FETCH_SUPPORTS_STARTED'; +const FETCH_SUPPORTS_COMPLETED = 'FETCH_SUPPORTS_COMPLETED'; +const ABANDON_SUPPORT_STARTED = 'ABANDON_SUPPORT_STARTED'; +const ABANDON_SUPPORT_COMPLETED = 'ABANDON_SUPPORT_COMPLETED'; const UPDATE_BALANCE = 'UPDATE_BALANCE'; const UPDATE_TOTAL_BALANCE = 'UPDATE_TOTAL_BALANCE'; const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED'; @@ -254,6 +258,10 @@ var action_types = /*#__PURE__*/Object.freeze({ GET_NEW_ADDRESS_COMPLETED: GET_NEW_ADDRESS_COMPLETED, FETCH_TRANSACTIONS_STARTED: FETCH_TRANSACTIONS_STARTED, FETCH_TRANSACTIONS_COMPLETED: FETCH_TRANSACTIONS_COMPLETED, + FETCH_SUPPORTS_STARTED: FETCH_SUPPORTS_STARTED, + FETCH_SUPPORTS_COMPLETED: FETCH_SUPPORTS_COMPLETED, + ABANDON_SUPPORT_STARTED: ABANDON_SUPPORT_STARTED, + ABANDON_SUPPORT_COMPLETED: ABANDON_SUPPORT_COMPLETED, UPDATE_BALANCE: UPDATE_BALANCE, UPDATE_TOTAL_BALANCE: UPDATE_TOTAL_BALANCE, CHECK_ADDRESS_IS_MINE_STARTED: CHECK_ADDRESS_IS_MINE_STARTED, @@ -647,6 +655,7 @@ const Lbry = { address_unused: (params = {}) => daemonCallWithResult('address_unused', params), transaction_list: (params = {}) => daemonCallWithResult('transaction_list', params), utxo_release: (params = {}) => daemonCallWithResult('utxo_release', params), + support_abandon: (params = {}) => daemonCallWithResult('support_abandon', params), sync_hash: (params = {}) => daemonCallWithResult('sync_hash', params), sync_apply: (params = {}) => daemonCallWithResult('sync_apply', params), @@ -1421,7 +1430,9 @@ const selectBalance = reselect.createSelector(selectState$2, state => state.bala const selectTotalBalance = reselect.createSelector(selectState$2, state => state.totalBalance); -const selectTransactionsById = reselect.createSelector(selectState$2, state => state.transactions); +const selectTransactionsById = reselect.createSelector(selectState$2, state => state.transactions || {}); + +const selectSupportsById = reselect.createSelector(selectState$2, state => state.supports || {}); const selectTransactionItems = reselect.createSelector(selectTransactionsById, byId => { const items = []; @@ -1608,6 +1619,7 @@ function doTotalBalanceSubscribe() { function doFetchTransactions() { return dispatch => { + dispatch(doFetchSupports()); dispatch({ type: FETCH_TRANSACTIONS_STARTED }); @@ -1623,6 +1635,23 @@ function doFetchTransactions() { }; } +function doFetchSupports() { + return dispatch => { + dispatch({ + type: FETCH_SUPPORTS_STARTED + }); + + lbryProxy.support_list().then(results => { + dispatch({ + type: FETCH_SUPPORTS_COMPLETED, + data: { + supports: results + } + }); + }); + }; +} + function doGetNewAddress() { return dispatch => { dispatch({ @@ -1966,39 +1995,43 @@ function doAbandonClaim(txid, nout) { return (dispatch, getState) => { const state = getState(); const myClaims = selectMyClaimsRaw(state); - const claimToAbandon = myClaims.find(claim => claim.txid === txid && claim.nout === nout); + const mySupports = selectSupportsById(state); - if (!claimToAbandon) { - console.error('No associated claim with txid: ', txid); + // A user could be trying to abandon a support or one of their claims + const claimToAbandon = myClaims.find(claim => claim.txid === txid && claim.nout === nout); + const supportToAbandon = mySupports[txid]; + + if (!claimToAbandon && !supportToAbandon) { + console.error('No associated support or claim with txid: ', txid); return; } - const { claim_id: claimId, name: claimName } = claimToAbandon; + const data = claimToAbandon ? { claimId: claimToAbandon.claim_id } : { txid: supportToAbandon.txid }; + + const isClaim = !!claimToAbandon; + const startedActionType = isClaim ? ABANDON_CLAIM_STARTED : ABANDON_SUPPORT_STARTED; + const completedActionType = isClaim ? ABANDON_CLAIM_STARTED : ABANDON_SUPPORT_COMPLETED; dispatch({ - type: ABANDON_CLAIM_STARTED, - data: { - claimId - } + type: startedActionType, + data }); const errorCallback = () => { dispatch(doToast({ - message: 'Error abandoning claim', + message: isClaim ? 'Error abandoning your claim' : 'Error unlocking your tip', isError: true })); }; const successCallback = () => { dispatch({ - type: ABANDON_CLAIM_SUCCEEDED, - data: { - claimId - } + type: completedActionType, + data }); dispatch(doToast({ - message: 'Successfully abandoned your claim' + message: isClaim ? 'Successfully abandoned your claim' : 'Successfully unlocked your tip!' })); // After abandoning, call claim_list to show the claim as abandoned @@ -2013,7 +2046,19 @@ function doAbandonClaim(txid, nout) { blocking: true }; - const method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon'; + let method; + if (supportToAbandon) { + method = 'support_abandon'; + } else if (claimToAbandon) { + const { name: claimName } = claimToAbandon; + method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon'; + } + + if (!method) { + console.error('No "method" chosen for claim or support abandon'); + return; + } + lbryProxy[method](abandonParams).then(successCallback, errorCallback); }; } @@ -3094,9 +3139,8 @@ const searchReducer = handleActions({ } }, defaultState$3); -// +var _extends$7 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; -const reducers$2 = {}; const buildDraftTransaction = () => ({ amount: undefined, address: undefined @@ -3109,10 +3153,12 @@ const buildDraftTransaction = () => ({ const defaultState$4 = { balance: undefined, totalBalance: undefined, - blocks: {}, latestBlock: undefined, transactions: {}, fetchingTransactions: false, + supports: {}, + fetchingSupports: false, + abandoningSupportsById: {}, gettingNewAddress: false, draftTransaction: buildDraftTransaction(), sendingSupport: false, @@ -3132,217 +3178,234 @@ const defaultState$4 = { transactionListFilter: 'all' }; -reducers$2[FETCH_TRANSACTIONS_STARTED] = state => Object.assign({}, state, { - fetchingTransactions: true -}); +const walletReducer = handleActions({ + [FETCH_TRANSACTIONS_STARTED]: state => _extends$7({}, state, { + fetchingTransactions: true + }), -reducers$2[FETCH_TRANSACTIONS_COMPLETED] = (state, action) => { - const byId = Object.assign({}, state.transactions); + [FETCH_TRANSACTIONS_COMPLETED]: (state, action) => { + const byId = _extends$7({}, state.transactions); - const { transactions } = action.data; + const { transactions } = action.data; + transactions.forEach(transaction => { + byId[transaction.txid] = transaction; + }); - transactions.forEach(transaction => { - byId[transaction.txid] = transaction; - }); + return _extends$7({}, state, { + transactions: byId, + fetchingTransactions: false + }); + }, - return Object.assign({}, state, { - transactions: byId, - fetchingTransactions: false - }); -}; + [FETCH_SUPPORTS_STARTED]: state => _extends$7({}, state, { + fetchingSupports: true + }), -reducers$2[GET_NEW_ADDRESS_STARTED] = state => Object.assign({}, state, { - gettingNewAddress: true -}); + [FETCH_SUPPORTS_COMPLETED]: (state, action) => { + const byId = state.supports; + const { supports } = action.data; -reducers$2[GET_NEW_ADDRESS_COMPLETED] = (state, action) => { - const { address } = action.data; + supports.forEach(support => { + byId[support.txid] = support; + }); - // Say no to localStorage! - return Object.assign({}, state, { - gettingNewAddress: false, - receiveAddress: address - }); -}; + return _extends$7({}, state, { supports: byId, fetchingSupports: false }); + }, -reducers$2[UPDATE_BALANCE] = (state, action) => Object.assign({}, state, { - balance: action.data.balance -}); + [ABANDON_SUPPORT_STARTED]: (state, action) => { + const { txid } = action.data; + const abandoningById = state.abandoningSupportsById; -reducers$2[UPDATE_TOTAL_BALANCE] = (state, action) => Object.assign({}, state, { - totalBalance: action.data.totalBalance -}); + abandoningById[txid] = true; -reducers$2[CHECK_ADDRESS_IS_MINE_STARTED] = state => Object.assign({}, state, { - checkingAddressOwnership: true -}); + return _extends$7({}, state, { + abandoningSupportsById: abandoningById + }); + }, -reducers$2[CHECK_ADDRESS_IS_MINE_COMPLETED] = state => Object.assign({}, state, { - checkingAddressOwnership: false -}); + [ABANDON_SUPPORT_COMPLETED]: (state, action) => { + const { txid } = action.data; + const byId = state.supports; + const abandoningById = state.abandoningSupportsById; -reducers$2[SET_DRAFT_TRANSACTION_AMOUNT] = (state, action) => { - const oldDraft = state.draftTransaction; - const newDraft = Object.assign({}, oldDraft, { - amount: parseFloat(action.data.amount) - }); + delete abandoningById[txid]; + delete byId[txid]; - return Object.assign({}, state, { - draftTransaction: newDraft - }); -}; + return _extends$7({}, state, { + supports: byId, + abandoningSupportsById: abandoningById + }); + }, -reducers$2[SET_DRAFT_TRANSACTION_ADDRESS] = (state, action) => { - const oldDraft = state.draftTransaction; - const newDraft = Object.assign({}, oldDraft, { - address: action.data.address - }); + [GET_NEW_ADDRESS_STARTED]: state => _extends$7({}, state, { + gettingNewAddress: true + }), - return Object.assign({}, state, { - draftTransaction: newDraft - }); -}; + [GET_NEW_ADDRESS_COMPLETED]: (state, action) => { + const { address } = action.data; -reducers$2[SEND_TRANSACTION_STARTED] = state => { - const newDraftTransaction = Object.assign({}, state.draftTransaction, { - sending: true - }); + return _extends$7({}, state, { gettingNewAddress: false, receiveAddress: address }); + }, - return Object.assign({}, state, { - draftTransaction: newDraftTransaction - }); -}; + [UPDATE_BALANCE]: (state, action) => _extends$7({}, state, { + balance: action.data.balance + }), -reducers$2[SEND_TRANSACTION_COMPLETED] = state => Object.assign({}, state, { - draftTransaction: buildDraftTransaction() -}); + [UPDATE_TOTAL_BALANCE]: (state, action) => _extends$7({}, state, { + totalBalance: action.data.totalBalance + }), -reducers$2[SEND_TRANSACTION_FAILED] = (state, action) => { - const newDraftTransaction = Object.assign({}, state.draftTransaction, { - sending: false, - error: action.data.error - }); + [CHECK_ADDRESS_IS_MINE_STARTED]: state => _extends$7({}, state, { + checkingAddressOwnership: true + }), - return Object.assign({}, state, { - draftTransaction: newDraftTransaction - }); -}; + [CHECK_ADDRESS_IS_MINE_COMPLETED]: state => _extends$7({}, state, { + checkingAddressOwnership: false + }), -reducers$2[SUPPORT_TRANSACTION_STARTED] = state => Object.assign({}, state, { - sendingSupport: true -}); + [SET_DRAFT_TRANSACTION_AMOUNT]: (state, action) => { + const oldDraft = state.draftTransaction; + const newDraft = _extends$7({}, oldDraft, { amount: parseFloat(action.data.amount) }); -reducers$2[SUPPORT_TRANSACTION_COMPLETED] = state => Object.assign({}, state, { - sendingSupport: false -}); + return _extends$7({}, state, { draftTransaction: newDraft }); + }, -reducers$2[SUPPORT_TRANSACTION_FAILED] = (state, action) => Object.assign({}, state, { - error: action.data.error, - sendingSupport: false -}); + [SET_DRAFT_TRANSACTION_ADDRESS]: (state, action) => { + const oldDraft = state.draftTransaction; + const newDraft = _extends$7({}, oldDraft, { address: action.data.address }); -reducers$2[WALLET_STATUS_COMPLETED] = (state, action) => Object.assign({}, state, { - walletIsEncrypted: action.result -}); + return _extends$7({}, state, { draftTransaction: newDraft }); + }, -reducers$2[WALLET_ENCRYPT_START] = state => Object.assign({}, state, { - walletEncryptPending: true, - walletEncryptSucceded: null, - walletEncryptResult: null -}); + [SEND_TRANSACTION_STARTED]: state => { + const newDraftTransaction = _extends$7({}, state.draftTransaction, { sending: true }); -reducers$2[WALLET_ENCRYPT_COMPLETED] = (state, action) => Object.assign({}, state, { - walletEncryptPending: false, - walletEncryptSucceded: true, - walletEncryptResult: action.result -}); + return _extends$7({}, state, { draftTransaction: newDraftTransaction }); + }, -reducers$2[WALLET_ENCRYPT_FAILED] = (state, action) => Object.assign({}, state, { - walletEncryptPending: false, - walletEncryptSucceded: false, - walletEncryptResult: action.result -}); + [SEND_TRANSACTION_COMPLETED]: state => Object.assign({}, state, { + draftTransaction: buildDraftTransaction() + }), -reducers$2[WALLET_DECRYPT_START] = state => Object.assign({}, state, { - walletDecryptPending: true, - walletDecryptSucceded: null, - walletDecryptResult: null -}); + [SEND_TRANSACTION_FAILED]: (state, action) => { + const newDraftTransaction = Object.assign({}, state.draftTransaction, { + sending: false, + error: action.data.error + }); -reducers$2[WALLET_DECRYPT_COMPLETED] = (state, action) => Object.assign({}, state, { - walletDecryptPending: false, - walletDecryptSucceded: true, - walletDecryptResult: action.result -}); + return _extends$7({}, state, { draftTransaction: newDraftTransaction }); + }, -reducers$2[WALLET_DECRYPT_FAILED] = (state, action) => Object.assign({}, state, { - walletDecryptPending: false, - walletDecryptSucceded: false, - walletDecryptResult: action.result -}); + [SUPPORT_TRANSACTION_STARTED]: state => _extends$7({}, state, { + sendingSupport: true + }), -reducers$2[WALLET_UNLOCK_START] = state => Object.assign({}, state, { - walletUnlockPending: true, - walletUnlockSucceded: null, - walletUnlockResult: null -}); + [SUPPORT_TRANSACTION_COMPLETED]: state => _extends$7({}, state, { + sendingSupport: false + }), -reducers$2[WALLET_UNLOCK_COMPLETED] = (state, action) => Object.assign({}, state, { - walletUnlockPending: false, - walletUnlockSucceded: true, - walletUnlockResult: action.result -}); + [SUPPORT_TRANSACTION_FAILED]: (state, action) => _extends$7({}, state, { + error: action.data.error, + sendingSupport: false + }), -reducers$2[WALLET_UNLOCK_FAILED] = (state, action) => Object.assign({}, state, { - walletUnlockPending: false, - walletUnlockSucceded: false, - walletUnlockResult: action.result -}); + [WALLET_STATUS_COMPLETED]: (state, action) => _extends$7({}, state, { + walletIsEncrypted: action.result + }), -reducers$2[WALLET_LOCK_START] = state => Object.assign({}, state, { - walletLockPending: false, - walletLockSucceded: null, - walletLockResult: null -}); + [WALLET_ENCRYPT_START]: state => _extends$7({}, state, { + walletEncryptPending: true, + walletEncryptSucceded: null, + walletEncryptResult: null + }), -reducers$2[WALLET_LOCK_COMPLETED] = (state, action) => Object.assign({}, state, { - walletLockPending: false, - walletLockSucceded: true, - walletLockResult: action.result -}); + [WALLET_ENCRYPT_COMPLETED]: (state, action) => _extends$7({}, state, { + walletEncryptPending: false, + walletEncryptSucceded: true, + walletEncryptResult: action.result + }), -reducers$2[WALLET_LOCK_FAILED] = (state, action) => Object.assign({}, state, { - walletLockPending: false, - walletLockSucceded: false, - walletLockResult: action.result -}); + [WALLET_ENCRYPT_FAILED]: (state, action) => _extends$7({}, state, { + walletEncryptPending: false, + walletEncryptSucceded: false, + walletEncryptResult: action.result + }), -reducers$2[SET_TRANSACTION_LIST_FILTER] = (state, action) => Object.assign({}, state, { - transactionListFilter: action.data -}); + [WALLET_DECRYPT_START]: state => _extends$7({}, state, { + walletDecryptPending: true, + walletDecryptSucceded: null, + walletDecryptResult: null + }), -reducers$2[UPDATE_CURRENT_HEIGHT] = (state, action) => Object.assign({}, state, { - latestBlock: action.data -}); + [WALLET_DECRYPT_COMPLETED]: (state, action) => _extends$7({}, state, { + walletDecryptPending: false, + walletDecryptSucceded: true, + walletDecryptResult: action.result + }), -function walletReducer(state = defaultState$4, action) { - const handler = reducers$2[action.type]; - if (handler) return handler(state, action); - return state; -} + [WALLET_DECRYPT_FAILED]: (state, action) => _extends$7({}, state, { + walletDecryptPending: false, + walletDecryptSucceded: false, + walletDecryptResult: action.result + }), -var _extends$7 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + [WALLET_UNLOCK_START]: state => _extends$7({}, state, { + walletUnlockPending: true, + walletUnlockSucceded: null, + walletUnlockResult: null + }), -const reducers$3 = {}; + [WALLET_UNLOCK_COMPLETED]: (state, action) => _extends$7({}, state, { + walletUnlockPending: false, + walletUnlockSucceded: true, + walletUnlockResult: action.result + }), + + [WALLET_UNLOCK_FAILED]: (state, action) => _extends$7({}, state, { + walletUnlockPending: false, + walletUnlockSucceded: false, + walletUnlockResult: action.result + }), + + [WALLET_LOCK_START]: state => _extends$7({}, state, { + walletLockPending: false, + walletLockSucceded: null, + walletLockResult: null + }), + + [WALLET_LOCK_COMPLETED]: (state, action) => _extends$7({}, state, { + walletLockPending: false, + walletLockSucceded: true, + walletLockResult: action.result + }), + + [WALLET_LOCK_FAILED]: (state, action) => _extends$7({}, state, { + walletLockPending: false, + walletLockSucceded: false, + walletLockResult: action.result + }), + + [SET_TRANSACTION_LIST_FILTER]: (state, action) => _extends$7({}, state, { + transactionListFilter: action.data + }), + + [UPDATE_CURRENT_HEIGHT]: (state, action) => _extends$7({}, state, { + latestBlock: action.data + }) +}, defaultState$4); + +var _extends$8 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +const reducers$2 = {}; const defaultState$5 = { positions: {} }; -reducers$3[SET_CONTENT_POSITION] = (state, action) => { +reducers$2[SET_CONTENT_POSITION] = (state, action) => { const { claimId, outpoint, position } = action.data; - return _extends$7({}, state, { - positions: _extends$7({}, state.positions, { - [claimId]: _extends$7({}, state.positions[claimId], { + return _extends$8({}, state, { + positions: _extends$8({}, state.positions, { + [claimId]: _extends$8({}, state.positions[claimId], { [outpoint]: position }) }) @@ -3350,7 +3413,7 @@ reducers$3[SET_CONTENT_POSITION] = (state, action) => { }; function contentReducer(state = defaultState$5, action) { - const handler = reducers$3[action.type]; + const handler = reducers$2[action.type]; if (handler) return handler(state, action); return state; } @@ -3366,14 +3429,14 @@ const makeSelectContentPositionForUri = uri => reselect.createSelector(selectSta return state.positions[id] ? state.positions[id][outpoint] : null; }); -var _extends$8 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +var _extends$9 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; const selectState$5 = state => state.notifications || {}; const selectToast = reselect.createSelector(selectState$5, state => { if (state.toasts.length) { const { id, params } = state.toasts[0]; - return _extends$8({ + return _extends$9({ id }, params); } @@ -3537,6 +3600,7 @@ exports.selectSearchState = selectState; exports.selectSearchSuggestions = selectSearchSuggestions; exports.selectSearchUrisByQuery = selectSearchUrisByQuery; exports.selectSearchValue = selectSearchValue; +exports.selectSupportsById = selectSupportsById; exports.selectToast = selectToast; exports.selectTotalBalance = selectTotalBalance; exports.selectTotalDownloadProgress = selectTotalDownloadProgress; diff --git a/dist/flow-typed/Lbry.js b/dist/flow-typed/Lbry.js index 93ce160..6219328 100644 --- a/dist/flow-typed/Lbry.js +++ b/dist/flow-typed/Lbry.js @@ -130,6 +130,8 @@ declare type SyncApplyResponse = { data: string, }; +declare type SupportAbandonResponse = GenericTxResponse; + // // Types used in the generic Lbry object that is exported // @@ -175,6 +177,7 @@ declare type LbryTypes = { address_is_mine: (params: {}) => Promise, address_unused: (params: {}) => Promise, // New address transaction_list: (params: {}) => Promise, + support_abandon: (params: {}) => Promise, // Sync sync_hash: (params: {}) => Promise, diff --git a/dist/flow-typed/Transaction.js b/dist/flow-typed/Transaction.js index 7953b90..0042576 100644 --- a/dist/flow-typed/Transaction.js +++ b/dist/flow-typed/Transaction.js @@ -9,3 +9,19 @@ declare type Transaction = { type: string, date: Date, }; + +declare type Support = { + address: string, + amount: string, + claim_id: string, + confirmations: number, + height: string, + is_change: string, + is_mine: string, + name: string, + nout: string, + permanent_url: string, + timestamp: number, + txid: string, + type: string, +}; diff --git a/flow-typed/Lbry.js b/flow-typed/Lbry.js index 93ce160..6219328 100644 --- a/flow-typed/Lbry.js +++ b/flow-typed/Lbry.js @@ -130,6 +130,8 @@ declare type SyncApplyResponse = { data: string, }; +declare type SupportAbandonResponse = GenericTxResponse; + // // Types used in the generic Lbry object that is exported // @@ -175,6 +177,7 @@ declare type LbryTypes = { address_is_mine: (params: {}) => Promise, address_unused: (params: {}) => Promise, // New address transaction_list: (params: {}) => Promise, + support_abandon: (params: {}) => Promise, // Sync sync_hash: (params: {}) => Promise, diff --git a/flow-typed/Transaction.js b/flow-typed/Transaction.js index 7953b90..0042576 100644 --- a/flow-typed/Transaction.js +++ b/flow-typed/Transaction.js @@ -9,3 +9,19 @@ declare type Transaction = { type: string, date: Date, }; + +declare type Support = { + address: string, + amount: string, + claim_id: string, + confirmations: number, + height: string, + is_change: string, + is_mine: string, + name: string, + nout: string, + permanent_url: string, + timestamp: number, + txid: string, + type: string, +}; diff --git a/src/constants/action_types.js b/src/constants/action_types.js index 37a94cc..95d4035 100644 --- a/src/constants/action_types.js +++ b/src/constants/action_types.js @@ -33,6 +33,10 @@ export const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED'; export const GET_NEW_ADDRESS_COMPLETED = 'GET_NEW_ADDRESS_COMPLETED'; export const FETCH_TRANSACTIONS_STARTED = 'FETCH_TRANSACTIONS_STARTED'; export const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED'; +export const FETCH_SUPPORTS_STARTED = 'FETCH_SUPPORTS_STARTED'; +export const FETCH_SUPPORTS_COMPLETED = 'FETCH_SUPPORTS_COMPLETED'; +export const ABANDON_SUPPORT_STARTED = 'ABANDON_SUPPORT_STARTED'; +export const ABANDON_SUPPORT_COMPLETED = 'ABANDON_SUPPORT_COMPLETED'; export const UPDATE_BALANCE = 'UPDATE_BALANCE'; export const UPDATE_TOTAL_BALANCE = 'UPDATE_TOTAL_BALANCE'; export const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED'; diff --git a/src/index.js b/src/index.js index 9c17d2d..cc751c0 100644 --- a/src/index.js +++ b/src/index.js @@ -183,6 +183,7 @@ export { selectBalance, selectTotalBalance, selectTransactionsById, + selectSupportsById, selectTransactionItems, selectRecentTransactions, selectHasTransactions, diff --git a/src/lbry.js b/src/lbry.js index 1d306bb..ec66286 100644 --- a/src/lbry.js +++ b/src/lbry.js @@ -86,6 +86,7 @@ const Lbry: LbryTypes = { address_unused: (params = {}) => daemonCallWithResult('address_unused', params), transaction_list: (params = {}) => daemonCallWithResult('transaction_list', params), utxo_release: (params = {}) => daemonCallWithResult('utxo_release', params), + support_abandon: (params = {}) => daemonCallWithResult('support_abandon', params), sync_hash: (params = {}) => daemonCallWithResult('sync_hash', params), sync_apply: (params = {}) => daemonCallWithResult('sync_apply', params), diff --git a/src/redux/actions/claims.js b/src/redux/actions/claims.js index 32318c5..3e2ba1b 100644 --- a/src/redux/actions/claims.js +++ b/src/redux/actions/claims.js @@ -5,6 +5,7 @@ import { normalizeURI, parseURI } from 'lbryURI'; import { doToast } from 'redux/actions/notifications'; import { selectMyClaimsRaw, selectResolvingUris, selectClaimsByUri } from 'redux/selectors/claims'; import { doFetchTransactions } from 'redux/actions/wallet'; +import { selectSupportsById } from 'redux/selectors/wallet'; import { creditsToString } from 'util/formatCredits'; export function doResolveUris(uris: Array, returnCachedClaims: boolean = false) { @@ -92,26 +93,38 @@ export function doAbandonClaim(txid: string, nout: number) { return (dispatch: Dispatch, getState: GetState) => { const state = getState(); const myClaims: Array = selectMyClaimsRaw(state); - const claimToAbandon = myClaims.find(claim => claim.txid === txid && claim.nout === nout); + const mySupports: { [string]: Support } = selectSupportsById(state); - if (!claimToAbandon) { - console.error('No associated claim with txid: ', txid); + // A user could be trying to abandon a support or one of their claims + const claimToAbandon = myClaims.find(claim => claim.txid === txid && claim.nout === nout); + const supportToAbandon = mySupports[txid]; + + if (!claimToAbandon && !supportToAbandon) { + console.error('No associated support or claim with txid: ', txid); return; } - const { claim_id: claimId, name: claimName } = claimToAbandon; + const data = claimToAbandon + ? { claimId: claimToAbandon.claim_id } + : { txid: supportToAbandon.txid }; + + const isClaim = !!claimToAbandon; + const startedActionType = isClaim + ? ACTIONS.ABANDON_CLAIM_STARTED + : ACTIONS.ABANDON_SUPPORT_STARTED; + const completedActionType = isClaim + ? ACTIONS.ABANDON_CLAIM_STARTED + : ACTIONS.ABANDON_SUPPORT_COMPLETED; dispatch({ - type: ACTIONS.ABANDON_CLAIM_STARTED, - data: { - claimId, - }, + type: startedActionType, + data, }); const errorCallback = () => { dispatch( doToast({ - message: 'Error abandoning claim', + message: isClaim ? 'Error abandoning your claim' : 'Error unlocking your tip', isError: true, }) ); @@ -119,15 +132,15 @@ export function doAbandonClaim(txid: string, nout: number) { const successCallback = () => { dispatch({ - type: ACTIONS.ABANDON_CLAIM_SUCCEEDED, - data: { - claimId, - }, + type: completedActionType, + data, }); dispatch( doToast({ - message: 'Successfully abandoned your claim', + message: isClaim + ? 'Successfully abandoned your claim' + : 'Successfully unlocked your tip!', }) ); @@ -143,7 +156,19 @@ export function doAbandonClaim(txid: string, nout: number) { blocking: true, }; - const method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon'; + let method; + if (supportToAbandon) { + method = 'support_abandon'; + } else if (claimToAbandon) { + const { name: claimName } = claimToAbandon; + method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon'; + } + + if (!method) { + console.error('No "method" chosen for claim or support abandon'); + return; + } + Lbry[method](abandonParams).then(successCallback, errorCallback); }; } diff --git a/src/redux/actions/wallet.js b/src/redux/actions/wallet.js index cdcd816..74c9126 100644 --- a/src/redux/actions/wallet.js +++ b/src/redux/actions/wallet.js @@ -63,6 +63,7 @@ export function doTotalBalanceSubscribe() { export function doFetchTransactions() { return dispatch => { + dispatch(doFetchSupports()); dispatch({ type: ACTIONS.FETCH_TRANSACTIONS_STARTED, }); @@ -80,6 +81,23 @@ export function doFetchTransactions() { }; } +export function doFetchSupports() { + return dispatch => { + dispatch({ + type: ACTIONS.FETCH_SUPPORTS_STARTED, + }); + + Lbry.support_list().then(results => { + dispatch({ + type: ACTIONS.FETCH_SUPPORTS_COMPLETED, + data: { + supports: results, + }, + }); + }); + }; +} + export function doGetNewAddress() { return dispatch => { dispatch({ diff --git a/src/redux/reducers/wallet.js b/src/redux/reducers/wallet.js index 5e9061b..259479d 100644 --- a/src/redux/reducers/wallet.js +++ b/src/redux/reducers/wallet.js @@ -1,7 +1,7 @@ // @flow import * as ACTIONS from 'constants/action_types'; +import { handleActions } from 'util/redux-utils'; -const reducers = {}; const buildDraftTransaction = () => ({ amount: undefined, address: undefined, @@ -16,9 +16,10 @@ type ActionResult = { type WalletState = { balance: any, - blocks: any, latestBlock: ?number, - transactions: any, + transactions: { [string]: Transaction }, + supports: { [string]: Support }, + abandoningSupportsById: { [string]: boolean }, fetchingTransactions: boolean, gettingNewAddress: boolean, draftTransaction: any, @@ -41,10 +42,12 @@ type WalletState = { const defaultState = { balance: undefined, totalBalance: undefined, - blocks: {}, latestBlock: undefined, transactions: {}, fetchingTransactions: false, + supports: {}, + fetchingSupports: false, + abandoningSupportsById: {}, gettingNewAddress: false, draftTransaction: buildDraftTransaction(), sendingSupport: false, @@ -64,226 +67,250 @@ const defaultState = { transactionListFilter: 'all', }; -reducers[ACTIONS.FETCH_TRANSACTIONS_STARTED] = (state: WalletState) => - Object.assign({}, state, { - fetchingTransactions: true, - }); +export const walletReducer = handleActions( + { + [ACTIONS.FETCH_TRANSACTIONS_STARTED]: (state: WalletState) => ({ + ...state, + fetchingTransactions: true, + }), -reducers[ACTIONS.FETCH_TRANSACTIONS_COMPLETED] = (state: WalletState, action) => { - const byId = Object.assign({}, state.transactions); + [ACTIONS.FETCH_TRANSACTIONS_COMPLETED]: (state: WalletState, action) => { + const byId = { ...state.transactions }; - const { transactions } = action.data; + const { transactions } = action.data; + transactions.forEach(transaction => { + byId[transaction.txid] = transaction; + }); - transactions.forEach(transaction => { - byId[transaction.txid] = transaction; - }); + return { + ...state, + transactions: byId, + fetchingTransactions: false, + }; + }, - return Object.assign({}, state, { - transactions: byId, - fetchingTransactions: false, - }); -}; + [ACTIONS.FETCH_SUPPORTS_STARTED]: (state: WalletState) => ({ + ...state, + fetchingSupports: true, + }), -reducers[ACTIONS.GET_NEW_ADDRESS_STARTED] = (state: WalletState) => - Object.assign({}, state, { - gettingNewAddress: true, - }); + [ACTIONS.FETCH_SUPPORTS_COMPLETED]: (state: WalletState, action) => { + const byId = state.supports; + const { supports } = action.data; -reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = (state: WalletState, action) => { - const { address } = action.data; + supports.forEach(support => { + byId[support.txid] = support; + }); - // Say no to localStorage! - return Object.assign({}, state, { - gettingNewAddress: false, - receiveAddress: address, - }); -}; + return { ...state, supports: byId, fetchingSupports: false }; + }, -reducers[ACTIONS.UPDATE_BALANCE] = (state: WalletState, action) => - Object.assign({}, state, { - balance: action.data.balance, - }); + [ACTIONS.ABANDON_SUPPORT_STARTED]: (state: WalletState, action: any): WalletState => { + const { txid }: { txid: string } = action.data; + const abandoningById = state.abandoningSupportsById; -reducers[ACTIONS.UPDATE_TOTAL_BALANCE] = (state: WalletState, action) => - Object.assign({}, state, { - totalBalance: action.data.totalBalance, - }); + abandoningById[txid] = true; -reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED] = (state: WalletState) => - Object.assign({}, state, { - checkingAddressOwnership: true, - }); + return { + ...state, + abandoningSupportsById: abandoningById, + }; + }, -reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED] = (state: WalletState) => - Object.assign({}, state, { - checkingAddressOwnership: false, - }); + [ACTIONS.ABANDON_SUPPORT_COMPLETED]: (state: WalletState, action: any): WalletState => { + const { txid }: { txid: string } = action.data; + const byId = state.supports; + const abandoningById = state.abandoningSupportsById; -reducers[ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT] = (state: WalletState, action) => { - const oldDraft = state.draftTransaction; - const newDraft = Object.assign({}, oldDraft, { - amount: parseFloat(action.data.amount), - }); + delete abandoningById[txid]; + delete byId[txid]; - return Object.assign({}, state, { - draftTransaction: newDraft, - }); -}; + return { + ...state, + supports: byId, + abandoningSupportsById: abandoningById, + }; + }, -reducers[ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS] = (state: WalletState, action) => { - const oldDraft = state.draftTransaction; - const newDraft = Object.assign({}, oldDraft, { - address: action.data.address, - }); + [ACTIONS.GET_NEW_ADDRESS_STARTED]: (state: WalletState) => ({ + ...state, + gettingNewAddress: true, + }), - return Object.assign({}, state, { - draftTransaction: newDraft, - }); -}; + [ACTIONS.GET_NEW_ADDRESS_COMPLETED]: (state: WalletState, action) => { + const { address } = action.data; -reducers[ACTIONS.SEND_TRANSACTION_STARTED] = (state: WalletState) => { - const newDraftTransaction = Object.assign({}, state.draftTransaction, { - sending: true, - }); + return { ...state, gettingNewAddress: false, receiveAddress: address }; + }, - return Object.assign({}, state, { - draftTransaction: newDraftTransaction, - }); -}; + [ACTIONS.UPDATE_BALANCE]: (state: WalletState, action) => ({ + ...state, + balance: action.data.balance, + }), -reducers[ACTIONS.SEND_TRANSACTION_COMPLETED] = (state: WalletState) => - Object.assign({}, state, { - draftTransaction: buildDraftTransaction(), - }); + [ACTIONS.UPDATE_TOTAL_BALANCE]: (state: WalletState, action) => ({ + ...state, + totalBalance: action.data.totalBalance, + }), -reducers[ACTIONS.SEND_TRANSACTION_FAILED] = (state: WalletState, action) => { - const newDraftTransaction = Object.assign({}, state.draftTransaction, { - sending: false, - error: action.data.error, - }); + [ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED]: (state: WalletState) => ({ + ...state, + checkingAddressOwnership: true, + }), - return Object.assign({}, state, { - draftTransaction: newDraftTransaction, - }); -}; + [ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED]: (state: WalletState) => ({ + ...state, + checkingAddressOwnership: false, + }), -reducers[ACTIONS.SUPPORT_TRANSACTION_STARTED] = (state: WalletState) => - Object.assign({}, state, { - sendingSupport: true, - }); + [ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT]: (state: WalletState, action) => { + const oldDraft = state.draftTransaction; + const newDraft = { ...oldDraft, amount: parseFloat(action.data.amount) }; -reducers[ACTIONS.SUPPORT_TRANSACTION_COMPLETED] = (state: WalletState) => - Object.assign({}, state, { - sendingSupport: false, - }); + return { ...state, draftTransaction: newDraft }; + }, -reducers[ACTIONS.SUPPORT_TRANSACTION_FAILED] = (state: WalletState, action) => - Object.assign({}, state, { - error: action.data.error, - sendingSupport: false, - }); + [ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS]: (state: WalletState, action) => { + const oldDraft = state.draftTransaction; + const newDraft = { ...oldDraft, address: action.data.address }; -reducers[ACTIONS.WALLET_STATUS_COMPLETED] = (state: WalletState, action) => - Object.assign({}, state, { - walletIsEncrypted: action.result, - }); + return { ...state, draftTransaction: newDraft }; + }, -reducers[ACTIONS.WALLET_ENCRYPT_START] = (state: WalletState) => - Object.assign({}, state, { - walletEncryptPending: true, - walletEncryptSucceded: null, - walletEncryptResult: null, - }); + [ACTIONS.SEND_TRANSACTION_STARTED]: (state: WalletState) => { + const newDraftTransaction = { ...state.draftTransaction, sending: true }; -reducers[ACTIONS.WALLET_ENCRYPT_COMPLETED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletEncryptPending: false, - walletEncryptSucceded: true, - walletEncryptResult: action.result, - }); + return { ...state, draftTransaction: newDraftTransaction }; + }, -reducers[ACTIONS.WALLET_ENCRYPT_FAILED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletEncryptPending: false, - walletEncryptSucceded: false, - walletEncryptResult: action.result, - }); + [ACTIONS.SEND_TRANSACTION_COMPLETED]: (state: WalletState) => + Object.assign({}, state, { + draftTransaction: buildDraftTransaction(), + }), -reducers[ACTIONS.WALLET_DECRYPT_START] = (state: WalletState) => - Object.assign({}, state, { - walletDecryptPending: true, - walletDecryptSucceded: null, - walletDecryptResult: null, - }); + [ACTIONS.SEND_TRANSACTION_FAILED]: (state: WalletState, action) => { + const newDraftTransaction = Object.assign({}, state.draftTransaction, { + sending: false, + error: action.data.error, + }); -reducers[ACTIONS.WALLET_DECRYPT_COMPLETED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletDecryptPending: false, - walletDecryptSucceded: true, - walletDecryptResult: action.result, - }); + return { ...state, draftTransaction: newDraftTransaction }; + }, -reducers[ACTIONS.WALLET_DECRYPT_FAILED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletDecryptPending: false, - walletDecryptSucceded: false, - walletDecryptResult: action.result, - }); + [ACTIONS.SUPPORT_TRANSACTION_STARTED]: (state: WalletState) => ({ + ...state, + sendingSupport: true, + }), -reducers[ACTIONS.WALLET_UNLOCK_START] = (state: WalletState) => - Object.assign({}, state, { - walletUnlockPending: true, - walletUnlockSucceded: null, - walletUnlockResult: null, - }); + [ACTIONS.SUPPORT_TRANSACTION_COMPLETED]: (state: WalletState) => ({ + ...state, + sendingSupport: false, + }), -reducers[ACTIONS.WALLET_UNLOCK_COMPLETED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletUnlockPending: false, - walletUnlockSucceded: true, - walletUnlockResult: action.result, - }); + [ACTIONS.SUPPORT_TRANSACTION_FAILED]: (state: WalletState, action) => ({ + ...state, + error: action.data.error, + sendingSupport: false, + }), -reducers[ACTIONS.WALLET_UNLOCK_FAILED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletUnlockPending: false, - walletUnlockSucceded: false, - walletUnlockResult: action.result, - }); + [ACTIONS.WALLET_STATUS_COMPLETED]: (state: WalletState, action) => ({ + ...state, + walletIsEncrypted: action.result, + }), -reducers[ACTIONS.WALLET_LOCK_START] = (state: WalletState) => - Object.assign({}, state, { - walletLockPending: false, - walletLockSucceded: null, - walletLockResult: null, - }); + [ACTIONS.WALLET_ENCRYPT_START]: (state: WalletState) => ({ + ...state, + walletEncryptPending: true, + walletEncryptSucceded: null, + walletEncryptResult: null, + }), -reducers[ACTIONS.WALLET_LOCK_COMPLETED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletLockPending: false, - walletLockSucceded: true, - walletLockResult: action.result, - }); + [ACTIONS.WALLET_ENCRYPT_COMPLETED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletEncryptPending: false, + walletEncryptSucceded: true, + walletEncryptResult: action.result, + }), -reducers[ACTIONS.WALLET_LOCK_FAILED] = (state: WalletState, action: ActionResult) => - Object.assign({}, state, { - walletLockPending: false, - walletLockSucceded: false, - walletLockResult: action.result, - }); + [ACTIONS.WALLET_ENCRYPT_FAILED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletEncryptPending: false, + walletEncryptSucceded: false, + walletEncryptResult: action.result, + }), -reducers[ACTIONS.SET_TRANSACTION_LIST_FILTER] = (state: WalletState, action: { data: string }) => - Object.assign({}, state, { - transactionListFilter: action.data, - }); + [ACTIONS.WALLET_DECRYPT_START]: (state: WalletState) => ({ + ...state, + walletDecryptPending: true, + walletDecryptSucceded: null, + walletDecryptResult: null, + }), -reducers[ACTIONS.UPDATE_CURRENT_HEIGHT] = (state: WalletState, action: { data: number }) => - Object.assign({}, state, { - latestBlock: action.data, - }); + [ACTIONS.WALLET_DECRYPT_COMPLETED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletDecryptPending: false, + walletDecryptSucceded: true, + walletDecryptResult: action.result, + }), -export function walletReducer(state: WalletState = defaultState, action: ActionResult) { - const handler = reducers[action.type]; - if (handler) return handler(state, action); - return state; -} + [ACTIONS.WALLET_DECRYPT_FAILED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletDecryptPending: false, + walletDecryptSucceded: false, + walletDecryptResult: action.result, + }), + + [ACTIONS.WALLET_UNLOCK_START]: (state: WalletState) => ({ + ...state, + walletUnlockPending: true, + walletUnlockSucceded: null, + walletUnlockResult: null, + }), + + [ACTIONS.WALLET_UNLOCK_COMPLETED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletUnlockPending: false, + walletUnlockSucceded: true, + walletUnlockResult: action.result, + }), + + [ACTIONS.WALLET_UNLOCK_FAILED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletUnlockPending: false, + walletUnlockSucceded: false, + walletUnlockResult: action.result, + }), + + [ACTIONS.WALLET_LOCK_START]: (state: WalletState) => ({ + ...state, + walletLockPending: false, + walletLockSucceded: null, + walletLockResult: null, + }), + + [ACTIONS.WALLET_LOCK_COMPLETED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletLockPending: false, + walletLockSucceded: true, + walletLockResult: action.result, + }), + + [ACTIONS.WALLET_LOCK_FAILED]: (state: WalletState, action: ActionResult) => ({ + ...state, + walletLockPending: false, + walletLockSucceded: false, + walletLockResult: action.result, + }), + + [ACTIONS.SET_TRANSACTION_LIST_FILTER]: (state: WalletState, action: { data: string }) => ({ + ...state, + transactionListFilter: action.data, + }), + + [ACTIONS.UPDATE_CURRENT_HEIGHT]: (state: WalletState, action: { data: number }) => ({ + ...state, + latestBlock: action.data, + }), + }, + defaultState +); diff --git a/src/redux/selectors/wallet.js b/src/redux/selectors/wallet.js index a79bfc4..8e80ca0 100644 --- a/src/redux/selectors/wallet.js +++ b/src/redux/selectors/wallet.js @@ -82,7 +82,12 @@ export const selectTotalBalance = createSelector( export const selectTransactionsById = createSelector( selectState, - state => state.transactions + state => state.transactions || {} +); + +export const selectSupportsById = createSelector( + selectState, + state => state.supports || {} ); export const selectTransactionItems = createSelector(