Add option to abandon claim when deleting published file

This commit is contained in:
6ea86b96 2017-06-29 14:44:34 +07:00 committed by Jeremy Kauffman
parent 1dea560cdf
commit 91359c2966
7 changed files with 136 additions and 31 deletions

View file

@ -4,10 +4,11 @@ import { doFetchClaimListMine } from "actions/content";
import { import {
selectClaimsByUri, selectClaimsByUri,
selectClaimListMineIsPending, selectClaimListMineIsPending,
selectMyClaimsOutpoints,
} from "selectors/claims"; } from "selectors/claims";
import { import {
selectFileListIsPending, selectFileListIsPending,
selectAllFileInfos, selectFileInfosByOutpoint,
selectUrisLoading, selectUrisLoading,
} from "selectors/file_info"; } from "selectors/file_info";
import { doCloseModal } from "actions/app"; 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) { 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({ dispatch({
type: types.FILE_DELETE, type: types.FILE_DELETE,
data: { data: {
@ -87,11 +121,6 @@ export function doDeleteFile(outpoint, deleteFromComputer) {
}, },
}); });
lbry.file_delete({
outpoint: outpoint,
delete_from_download_dir: deleteFromComputer,
});
dispatch(doCloseModal()); dispatch(doCloseModal());
}; };
} }

View file

@ -16,6 +16,7 @@ import {
doOpenFileInFolder, doOpenFileInFolder,
doDeleteFile, doDeleteFile,
} from "actions/file_info"; } from "actions/file_info";
import { makeSelectClaimForUriIsMine } from "selectors/claims";
import { doPurchaseUri, doLoadVideo } from "actions/content"; import { doPurchaseUri, doLoadVideo } from "actions/content";
import FileActions from "./view"; import FileActions from "./view";
@ -25,6 +26,7 @@ const makeSelect = () => {
const selectDownloadingForUri = makeSelectDownloadingForUri(); const selectDownloadingForUri = makeSelectDownloadingForUri();
const selectCostInfoForUri = makeSelectCostInfoForUri(); const selectCostInfoForUri = makeSelectCostInfoForUri();
const selectLoadingForUri = makeSelectLoadingForUri(); const selectLoadingForUri = makeSelectLoadingForUri();
const selectClaimForUriIsMine = makeSelectClaimForUriIsMine();
const select = (state, props) => ({ const select = (state, props) => ({
fileInfo: selectFileInfoForUri(state, props), fileInfo: selectFileInfoForUri(state, props),
@ -35,6 +37,7 @@ const makeSelect = () => {
downloading: selectDownloadingForUri(state, props), downloading: selectDownloadingForUri(state, props),
costInfo: selectCostInfoForUri(state, props), costInfo: selectCostInfoForUri(state, props),
loading: selectLoadingForUri(state, props), loading: selectLoadingForUri(state, props),
claimIsMine: selectClaimForUriIsMine(state, props),
}); });
return select; return select;
@ -45,9 +48,9 @@ const perform = dispatch => ({
closeModal: () => dispatch(doCloseModal()), closeModal: () => dispatch(doCloseModal()),
openInFolder: fileInfo => dispatch(doOpenFileInFolder(fileInfo)), openInFolder: fileInfo => dispatch(doOpenFileInFolder(fileInfo)),
openInShell: fileInfo => dispatch(doOpenFileInShell(fileInfo)), openInShell: fileInfo => dispatch(doOpenFileInShell(fileInfo)),
deleteFile: (fileInfo, deleteFromComputer) => { deleteFile: (fileInfo, deleteFromComputer, abandonClaim) => {
dispatch(doHistoryBack()); dispatch(doHistoryBack());
dispatch(doDeleteFile(fileInfo, deleteFromComputer)); dispatch(doDeleteFile(fileInfo, deleteFromComputer, abandonClaim));
}, },
openModal: modal => dispatch(doOpenModal(modal)), openModal: modal => dispatch(doOpenModal(modal)),
startDownload: uri => dispatch(doPurchaseUri(uri, "affirmPurchase")), startDownload: uri => dispatch(doPurchaseUri(uri, "affirmPurchase")),

View file

@ -13,6 +13,7 @@ class FileActions extends React.PureComponent {
this.state = { this.state = {
forceShowActions: false, forceShowActions: false,
deleteChecked: false, deleteChecked: false,
abandonClaimChecked: false,
}; };
} }
@ -43,6 +44,12 @@ class FileActions extends React.PureComponent {
}); });
} }
handleAbandonClaimCheckboxClicked(event) {
this.setState({
abandonClaimChecked: event.target.checked,
});
}
onAffirmPurchase() { onAffirmPurchase() {
this.props.closeModal(); this.props.closeModal();
this.props.loadVideo(this.props.uri); this.props.loadVideo(this.props.uri);
@ -64,9 +71,11 @@ class FileActions extends React.PureComponent {
startDownload, startDownload,
costInfo, costInfo,
loading, loading,
claimIsMine,
} = this.props; } = this.props;
const deleteChecked = this.state.deleteChecked, const deleteChecked = this.state.deleteChecked,
abandonClaimChecked = this.state.abandonClaimChecked,
metadata = fileInfo ? fileInfo.metadata : null, metadata = fileInfo ? fileInfo.metadata : null,
openInFolderMessage = platform.startsWith("Mac") openInFolderMessage = platform.startsWith("Mac")
? __("Open in Finder") ? __("Open in Finder")
@ -77,15 +86,19 @@ class FileActions extends React.PureComponent {
let content; let content;
if (loading || downloading) { if (loading || downloading) {
const progress = fileInfo && fileInfo.written_bytes const progress =
? fileInfo.written_bytes / fileInfo.total_bytes * 100 fileInfo && fileInfo.written_bytes
: 0, ? fileInfo.written_bytes / fileInfo.total_bytes * 100
: 0,
label = fileInfo label = fileInfo
? progress.toFixed(0) + __("% complete") ? progress.toFixed(0) + __("% complete")
: __("Connecting..."), : __("Connecting..."),
labelWithIcon = ( labelWithIcon = (
<span className="button__content"> <span className="button__content">
<Icon icon="icon-download" /><span>{label}</span> <Icon icon="icon-download" />
<span>
{label}
</span>
</span> </span>
); );
@ -174,9 +187,11 @@ class FileActions extends React.PureComponent {
onConfirmed={this.onAffirmPurchase.bind(this)} onConfirmed={this.onAffirmPurchase.bind(this)}
onAborted={closeModal} onAborted={closeModal}
> >
{__("This will purchase")} <strong>{title}</strong> {__("for")} {__("This will purchase")} <strong>{title}</strong> {__("for")}{" "}
{" "}<strong><FilePrice uri={uri} look="plain" /></strong> <strong>
{" "}{__("credits")}. <FilePrice uri={uri} look="plain" />
</strong>{" "}
{__("credits")}.
</Modal> </Modal>
<Modal <Modal
isOpen={modal == "notEnoughCredits"} isOpen={modal == "notEnoughCredits"}
@ -197,22 +212,36 @@ class FileActions extends React.PureComponent {
contentLabel={__("Not enough credits")} contentLabel={__("Not enough credits")}
type="confirm" type="confirm"
confirmButtonLabel={__("Remove")} confirmButtonLabel={__("Remove")}
onConfirmed={() => deleteFile(fileInfo.outpoint, deleteChecked)} onConfirmed={() =>
deleteFile(fileInfo.outpoint, deleteChecked, abandonClaimChecked)}
onAborted={closeModal} onAborted={closeModal}
> >
<p> <p>
{__("Are you sure you'd like to remove")} <cite>{title}</cite> {__("Are you sure you'd like to remove")} <cite>{title}</cite>{" "}
{" "}{__("from LBRY?")} {__("from LBRY?")}
</p> </p>
<label> <section>
<FormField <label>
type="checkbox" <FormField
checked={deleteChecked} type="checkbox"
onClick={this.handleDeleteCheckboxClicked.bind(this)} checked={deleteChecked}
/> onClick={this.handleDeleteCheckboxClicked.bind(this)}
{" "}{__("Delete this file from my computer")} />{" "}
</label> {__("Delete this file from my computer")}
</label>
</section>
{claimIsMine &&
<section>
<label>
<FormField
type="checkbox"
checked={abandonClaimChecked}
onClick={this.handleAbandonClaimCheckboxClicked.bind(this)}
/>{" "}
{__("Abandon the claim for this URI")}
</label>
</section>}
</Modal> </Modal>
</section> </section>
); );

View file

@ -62,6 +62,8 @@ export const PLAY_VIDEO_STARTED = "PLAY_VIDEO_STARTED";
export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED"; export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED";
export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED"; export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED";
export const FILE_DELETE = "FILE_DELETE"; export const FILE_DELETE = "FILE_DELETE";
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
export const ABANDON_CLAIM_COMPLETED = "ABANDON_CLAIM_COMPLETED";
// Search // Search
export const SEARCH_STARTED = "SEARCH_STARTED"; export const SEARCH_STARTED = "SEARCH_STARTED";

View file

@ -268,9 +268,10 @@ lbry.getClientSettings = function() {
var outSettings = {}; var outSettings = {};
for (let setting of Object.keys(lbry.defaultClientSettings)) { for (let setting of Object.keys(lbry.defaultClientSettings)) {
var localStorageVal = localStorage.getItem("setting_" + setting); var localStorageVal = localStorage.getItem("setting_" + setting);
outSettings[setting] = localStorageVal === null outSettings[setting] =
? lbry.defaultClientSettings[setting] localStorageVal === null
: JSON.parse(localStorageVal); ? lbry.defaultClientSettings[setting]
: JSON.parse(localStorageVal);
} }
return outSettings; 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._resolveXhrs = {};
lbry.resolve = function(params = {}) { lbry.resolve = function(params = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View file

@ -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) { export default function reducer(state = defaultState, action) {
const handler = reducers[action.type]; const handler = reducers[action.type];
if (handler) return handler(state, action); if (handler) return handler(state, action);

View file

@ -48,6 +48,18 @@ export const makeSelectClaimForUri = () => {
return createSelector(selectClaimForUri, claim => claim); 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) => { export const selectClaimsInChannelForUri = (state, props) => {
return selectAllClaimsByChannel(state)[props.uri]; return selectAllClaimsByChannel(state)[props.uri];
}; };
@ -95,7 +107,7 @@ export const selectClaimListMineIsPending = createSelector(
export const selectMyClaims = createSelector( export const selectMyClaims = createSelector(
_selectState, _selectState,
state => state.myClaims || new Set() state => new Set(state.myClaims)
); );
export const selectMyClaimsOutpoints = createSelector( export const selectMyClaimsOutpoints = createSelector(