diff --git a/CHANGELOG.md b/CHANGELOG.md index ee6ca0528..f22fa8404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Web UI version numbers should always match the corresponding version of LBRY App ## [Unreleased] ### Added - * + * Add setting to automatically purchase low-cost content without a confirmation dialog * ### Changed diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js index eb62506a2..e040eadfc 100644 --- a/ui/js/actions/content.js +++ b/ui/js/actions/content.js @@ -1,4 +1,5 @@ import * as types from "constants/action_types"; +import * as settings from "constants/settings"; import lbry from "lbry"; import lbryio from "lbryio"; import lbryuri from "lbryuri"; @@ -322,26 +323,55 @@ export function doPurchaseUri(uri) { const downloadingByOutpoint = selectDownloadingByOutpoint(state); const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint]; - const costInfo = makeSelectCostInfoForUri(uri)(state); - const { cost } = costInfo; - if ( - alreadyDownloading || - (fileInfo && fileInfo.completed && fileInfo.written_bytes > 0) - ) { - return; + function attemptPlay(cost, instantPurchaseMax = null) { + if (!instantPurchaseMax || cost > instantPurchaseMax) { + dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri })); + } else { + dispatch(doLoadVideo(uri)); + } } // we already fully downloaded the file. - if ( - cost === 0 || - (fileInfo && (fileInfo.completed || fileInfo.download_directory)) - ) { - return dispatch(doLoadVideo(uri)); + if (fileInfo && fileInfo.completed) { + // If written_bytes is false that means the user has deleted/moved the + // file manually on their file system, so we need to dispatch a + // doLoadVideo action to reconstruct the file from the blobs + if (!fileInfo.written_bytes) dispatch(doLoadVideo(uri)); + + return Promise.resolve(); } + // we are already downloading the file + if (alreadyDownloading) { + return Promise.resolve(); + } + + const costInfo = makeSelectCostInfoForUri(uri)(state); + const { cost } = costInfo; + if (cost > balance) { - return dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); + dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); + return Promise.resolve(); + } + + if ( + cost == 0 || + !lbry.getClientSetting(settings.INSTANT_PURCHASE_ENABLED) + ) { + attemptPlay(cost); + } else { + const instantPurchaseMax = lbry.getClientSetting( + settings.INSTANT_PURCHASE_MAX + ); + if (instantPurchaseMax.currency == "LBC") { + attemptPlay(cost, instantPurchaseMax.amount); + } else { + // Need to convert currency of instant purchase maximum before trying to play + lbryio.getExchangeRates().then(({ lbc_usd }) => { + attemptPlay(cost, instantPurchaseMax.amount / lbc_usd); + }); + } } return dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri })); diff --git a/ui/js/constants/settings.js b/ui/js/constants/settings.js index a1a2e04e2..9346cfb70 100644 --- a/ui/js/constants/settings.js +++ b/ui/js/constants/settings.js @@ -6,5 +6,7 @@ export const NEW_USER_ACKNOWLEDGED = "welcome_acknowledged"; export const LANGUAGE = "language"; export const SHOW_NSFW = "showNsfw"; export const SHOW_UNAVAILABLE = "showUnavailable"; +export const INSTANT_PURCHASE_ENABLED = "instantPurchaseEnabled"; +export const INSTANT_PURCHASE_MAX = "instantPurchaseMax"; export const THEME = "theme"; export const THEMES = "themes"; diff --git a/ui/js/lbry.js b/ui/js/lbry.js index 767d4479c..32770bcf1 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -41,6 +41,9 @@ let lbry = { language: "en", theme: "light", themes: [], + instantPurchaseMax: null, + instantPurchaseEnabled: false, + instantPurchaseMax: { currency: "LBC", amount: 0.1 }, }, }; diff --git a/ui/js/page/settings/index.js b/ui/js/page/settings/index.js index a44bf670c..c22cbdbe7 100644 --- a/ui/js/page/settings/index.js +++ b/ui/js/page/settings/index.js @@ -21,6 +21,13 @@ const select = state => ({ daemonSettings: selectDaemonSettings(state), showNsfw: makeSelectClientSetting(settings.SHOW_NSFW)(state), showUnavailable: makeSelectClientSetting(settings.SHOW_UNAVAILABLE)(state), + instantPurchaseEnabled: makeSelectClientSetting( + settings.INSTANT_PURCHASE_ENABLED + )(state), + instantPurchaseMax: makeSelectClientSetting(settings.INSTANT_PURCHASE_MAX)( + state + ), + showUnavailable: makeSelectClientSetting(settings.SHOW_UNAVAILABLE)(state), theme: makeSelectClientSetting(settings.THEME)(state), themes: makeSelectClientSetting(settings.THEMES)(state), language: selectCurrentLanguage(state), diff --git a/ui/js/page/settings/view.jsx b/ui/js/page/settings/view.jsx index c09c1be13..186abbb5c 100644 --- a/ui/js/page/settings/view.jsx +++ b/ui/js/page/settings/view.jsx @@ -13,6 +13,8 @@ class SettingsPage extends React.PureComponent { super(props); this.state = { + instantPurchaseEnabled: props.instantPurchaseEnabled, + instantPurchaseMax: props.instantPurchaseMax, clearingCache: false, }; } @@ -59,6 +61,22 @@ class SettingsPage extends React.PureComponent { this.props.setClientSetting(settings.THEME, value); } + oninstantPurchaseEnabledChange(enabled) { + this.props.setClientSetting(settings.INSTANT_PURCHASE_ENABLED, enabled); + + this.setState({ + instantPurchaseEnabled: enabled, + }); + } + + onInstantPurchaseMaxChange(newValue) { + this.props.setClientSetting(settings.INSTANT_PURCHASE_MAX, newValue); + + this.setState({ + instantPurchaseMax: newValue, + }); + } + // onMaxUploadPrefChange(isLimited) { // if (!isLimited) { // this.setDaemonSetting("max_upload", 0.0); @@ -113,6 +131,8 @@ class SettingsPage extends React.PureComponent { language, languages, showNsfw, + instantPurchaseEnabled, + instantPurchaseMax, showUnavailable, theme, themes, @@ -167,9 +187,14 @@ class SettingsPage extends React.PureComponent {
-

{__("Max Purchase Price")}

+

{__("Purchase Settings")}

+
+ +
+
+
+ +
+ { + this.oninstantPurchaseEnabledChange(false); + }} + /> +
+ { + this.oninstantPurchaseEnabledChange(true); + }} + /> + {this.state.instantPurchaseEnabled && + this.onInstantPurchaseMaxChange(val)} + defaultValue={this.state.instantPurchaseMax} + />} +
+
+ When this option is chosen, LBRY won't ask you to confirm + downloads below the given price. +
+
diff --git a/ui/js/reducers/settings.js b/ui/js/reducers/settings.js index 2656c03fd..1c6834a91 100644 --- a/ui/js/reducers/settings.js +++ b/ui/js/reducers/settings.js @@ -6,6 +6,10 @@ import lbry from "lbry"; const reducers = {}; const defaultState = { clientSettings: { + instantPurchaseEnabled: lbry.getClientSetting( + settings.INSTANT_PURCHASE_ENABLED + ), + instantPurchaseMax: lbry.getClientSetting(settings.INSTANT_PURCHASE_MAX), showNsfw: lbry.getClientSetting(settings.SHOW_NSFW), showUnavailable: lbry.getClientSetting(settings.SHOW_UNAVAILABLE), welcome_acknowledged: lbry.getClientSetting(settings.NEW_USER_ACKNOWLEDGED),