diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js index ba4c606db..9d3861fb7 100644 --- a/ui/js/actions/file_info.js +++ b/ui/js/actions/file_info.js @@ -4,10 +4,11 @@ import { doFetchClaimListMine } from "actions/content"; import { selectClaimsByUri, selectClaimListMineIsPending, + selectMyClaimsOutpoints, } from "selectors/claims"; import { selectFileListIsPending, - selectAllFileInfos, + selectFileInfosByOutpoint, selectUrisLoading, } from "selectors/file_info"; import { doCloseModal } from "actions/app"; @@ -78,8 +79,41 @@ export function doOpenFileInFolder(fileInfo) { }; } -export function doDeleteFile(outpoint, deleteFromComputer) { +export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) { return function(dispatch, getState) { + const state = getState(); + + lbry.file_delete({ + outpoint: outpoint, + delete_from_download_dir: deleteFromComputer, + }); + + // If the file is for a claim we published then also abandom the claim + const myClaimsOutpoints = selectMyClaimsOutpoints(state); + if (abandonClaim && myClaimsOutpoints.indexOf(outpoint) !== -1) { + const byOutpoint = selectFileInfosByOutpoint(state); + const fileInfo = byOutpoint[outpoint]; + + if (fileInfo) { + dispatch({ + type: types.ABANDON_CLAIM_STARTED, + data: { + claimId: fileInfo.claim_id, + }, + }); + + const success = () => { + dispatch({ + type: types.ABANDON_CLAIM_COMPLETED, + data: { + claimId: fileInfo.claim_id, + }, + }); + }; + lbry.claim_abandon({ claim_id: fileInfo.claim_id }).then(success); + } + } + dispatch({ type: types.FILE_DELETE, data: { @@ -87,11 +121,6 @@ export function doDeleteFile(outpoint, deleteFromComputer) { }, }); - lbry.file_delete({ - outpoint: outpoint, - delete_from_download_dir: deleteFromComputer, - }); - dispatch(doCloseModal()); }; } diff --git a/ui/js/component/fileActions/index.js b/ui/js/component/fileActions/index.js index 572783ee5..eb5fc7422 100644 --- a/ui/js/component/fileActions/index.js +++ b/ui/js/component/fileActions/index.js @@ -16,6 +16,7 @@ import { doOpenFileInFolder, doDeleteFile, } from "actions/file_info"; +import { makeSelectClaimForUriIsMine } from "selectors/claims"; import { doPurchaseUri, doLoadVideo } from "actions/content"; import FileActions from "./view"; @@ -25,6 +26,7 @@ const makeSelect = () => { const selectDownloadingForUri = makeSelectDownloadingForUri(); const selectCostInfoForUri = makeSelectCostInfoForUri(); const selectLoadingForUri = makeSelectLoadingForUri(); + const selectClaimForUriIsMine = makeSelectClaimForUriIsMine(); const select = (state, props) => ({ fileInfo: selectFileInfoForUri(state, props), @@ -35,6 +37,7 @@ const makeSelect = () => { downloading: selectDownloadingForUri(state, props), costInfo: selectCostInfoForUri(state, props), loading: selectLoadingForUri(state, props), + claimIsMine: selectClaimForUriIsMine(state, props), }); return select; @@ -45,9 +48,9 @@ const perform = dispatch => ({ closeModal: () => dispatch(doCloseModal()), openInFolder: fileInfo => dispatch(doOpenFileInFolder(fileInfo)), openInShell: fileInfo => dispatch(doOpenFileInShell(fileInfo)), - deleteFile: (fileInfo, deleteFromComputer) => { + deleteFile: (fileInfo, deleteFromComputer, abandonClaim) => { dispatch(doHistoryBack()); - dispatch(doDeleteFile(fileInfo, deleteFromComputer)); + dispatch(doDeleteFile(fileInfo, deleteFromComputer, abandonClaim)); }, openModal: modal => dispatch(doOpenModal(modal)), startDownload: uri => dispatch(doPurchaseUri(uri, "affirmPurchase")), diff --git a/ui/js/component/fileActions/view.jsx b/ui/js/component/fileActions/view.jsx index b78512be9..530987806 100644 --- a/ui/js/component/fileActions/view.jsx +++ b/ui/js/component/fileActions/view.jsx @@ -13,6 +13,7 @@ class FileActions extends React.PureComponent { this.state = { forceShowActions: false, deleteChecked: false, + abandonClaimChecked: false, }; } @@ -43,6 +44,12 @@ class FileActions extends React.PureComponent { }); } + handleAbandonClaimCheckboxClicked(event) { + this.setState({ + abandonClaimChecked: event.target.checked, + }); + } + onAffirmPurchase() { this.props.closeModal(); this.props.loadVideo(this.props.uri); @@ -64,9 +71,11 @@ class FileActions extends React.PureComponent { startDownload, costInfo, loading, + claimIsMine, } = this.props; const deleteChecked = this.state.deleteChecked, + abandonClaimChecked = this.state.abandonClaimChecked, metadata = fileInfo ? fileInfo.metadata : null, openInFolderMessage = platform.startsWith("Mac") ? __("Open in Finder") @@ -77,15 +86,19 @@ class FileActions extends React.PureComponent { let content; if (loading || downloading) { - const progress = fileInfo && fileInfo.written_bytes - ? fileInfo.written_bytes / fileInfo.total_bytes * 100 - : 0, + const progress = + fileInfo && fileInfo.written_bytes + ? fileInfo.written_bytes / fileInfo.total_bytes * 100 + : 0, label = fileInfo ? progress.toFixed(0) + __("% complete") : __("Connecting..."), labelWithIcon = ( - {label} + + + {label} + ); @@ -174,9 +187,11 @@ class FileActions extends React.PureComponent { onConfirmed={this.onAffirmPurchase.bind(this)} onAborted={closeModal} > - {__("This will purchase")} {title} {__("for")} - {" "} - {" "}{__("credits")}. + {__("This will purchase")} {title} {__("for")}{" "} + + + {" "} + {__("credits")}. deleteFile(fileInfo.outpoint, deleteChecked)} + onConfirmed={() => + deleteFile(fileInfo.outpoint, deleteChecked, abandonClaimChecked)} onAborted={closeModal} >

- {__("Are you sure you'd like to remove")} {title} - {" "}{__("from LBRY?")} + {__("Are you sure you'd like to remove")} {title}{" "} + {__("from LBRY?")}

- +
+ +
+ {claimIsMine && +
+ +
}
); diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js index a66ae5874..216c84762 100644 --- a/ui/js/constants/action_types.js +++ b/ui/js/constants/action_types.js @@ -62,6 +62,8 @@ export const PLAY_VIDEO_STARTED = "PLAY_VIDEO_STARTED"; export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED"; export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED"; export const FILE_DELETE = "FILE_DELETE"; +export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED"; +export const ABANDON_CLAIM_COMPLETED = "ABANDON_CLAIM_COMPLETED"; // Search export const SEARCH_STARTED = "SEARCH_STARTED"; diff --git a/ui/js/lbry.js b/ui/js/lbry.js index 262e957bf..accc5152c 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -268,9 +268,10 @@ lbry.getClientSettings = function() { var outSettings = {}; for (let setting of Object.keys(lbry.defaultClientSettings)) { var localStorageVal = localStorage.getItem("setting_" + setting); - outSettings[setting] = localStorageVal === null - ? lbry.defaultClientSettings[setting] - : JSON.parse(localStorageVal); + outSettings[setting] = + localStorageVal === null + ? lbry.defaultClientSettings[setting] + : JSON.parse(localStorageVal); } return outSettings; }; @@ -462,6 +463,12 @@ lbry.claim_list_mine = function(params = {}) { }); }; +lbry.claim_abandon = function(params = {}) { + return new Promise((resolve, reject) => { + apiCall("claim_abandon", params, resolve, reject); + }); +}; + lbry._resolveXhrs = {}; lbry.resolve = function(params = {}) { return new Promise((resolve, reject) => { diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js index a237947b6..7417bc2fb 100644 --- a/ui/js/reducers/claims.js +++ b/ui/js/reducers/claims.js @@ -80,6 +80,29 @@ reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) { }); }; +reducers[types.ABANDON_CLAIM_COMPLETED] = function(state, action) { + const { claimId } = action.data; + const myClaims = new Set(state.myClaims); + const byId = Object.assign({}, state.byId); + const claimsByUri = Object.assign({}, state.claimsByUri); + const uris = []; + + Object.keys(claimsByUri).forEach(uri => { + if (claimsByUri[uri] === claimId) { + delete claimsByUri[uri]; + } + }); + + delete byId[claimId]; + myClaims.delete(claimId); + + return Object.assign({}, state, { + myClaims, + byId, + claimsByUri, + }); +}; + export default function reducer(state = defaultState, action) { const handler = reducers[action.type]; if (handler) return handler(state, action); diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js index 7508c6ce1..b966375e9 100644 --- a/ui/js/selectors/claims.js +++ b/ui/js/selectors/claims.js @@ -48,6 +48,18 @@ export const makeSelectClaimForUri = () => { return createSelector(selectClaimForUri, claim => claim); }; +const selectClaimForUriIsMine = (state, props) => { + const uri = lbryuri.normalize(props.uri); + const claim = selectClaimsByUri(state)[uri]; + const myClaims = selectMyClaims(state); + + return myClaims.has(claim.claim_id); +}; + +export const makeSelectClaimForUriIsMine = () => { + return createSelector(selectClaimForUriIsMine, isMine => isMine); +}; + export const selectClaimsInChannelForUri = (state, props) => { return selectAllClaimsByChannel(state)[props.uri]; }; @@ -95,7 +107,7 @@ export const selectClaimListMineIsPending = createSelector( export const selectMyClaims = createSelector( _selectState, - state => state.myClaims || new Set() + state => new Set(state.myClaims) ); export const selectMyClaimsOutpoints = createSelector(