Add basics of Instant Purchase setting

Fix and simplify state management of Instant Purchas setting

Add Instant Purchase check to Watch page

Merge Max Purchase Price and Instant Purchase into one section

Wording still not finalized.

Add Instant Purchase setting names to constants

Support USD for Instant Purchase

On Settings page, use constants for new Instant Purchase settings

Convert Instant Purchase Maximum setting into FormRow

Update wording of Instant Purchase option and add helper text.

Wording still not final.

On Settings page, get Instant Purchase settings via selector

Update CHANGELOG.md
This commit is contained in:
Alex Liebowitz 2017-09-08 01:03:37 -04:00 committed by Jeremy Kauffman
parent 27448499dd
commit 93b1ab8ac6
8 changed files with 125 additions and 15 deletions

View file

@ -8,7 +8,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
## [Unreleased] ## [Unreleased]
### Added ### Added
* * Add setting to automatically purchase low-cost content without a confirmation dialog
* *
### Changed ### Changed

View file

@ -1,4 +1,5 @@
import * as types from "constants/action_types"; import * as types from "constants/action_types";
import * as settings from "constants/settings";
import lbry from "lbry"; import lbry from "lbry";
import lbryio from "lbryio"; import lbryio from "lbryio";
import lbryuri from "lbryuri"; import lbryuri from "lbryuri";
@ -322,26 +323,55 @@ export function doPurchaseUri(uri) {
const downloadingByOutpoint = selectDownloadingByOutpoint(state); const downloadingByOutpoint = selectDownloadingByOutpoint(state);
const alreadyDownloading = const alreadyDownloading =
fileInfo && !!downloadingByOutpoint[fileInfo.outpoint]; fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
const costInfo = makeSelectCostInfoForUri(uri)(state);
const { cost } = costInfo;
if ( function attemptPlay(cost, instantPurchaseMax = null) {
alreadyDownloading || if (!instantPurchaseMax || cost > instantPurchaseMax) {
(fileInfo && fileInfo.completed && fileInfo.written_bytes > 0) dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri }));
) { } else {
return; dispatch(doLoadVideo(uri));
}
} }
// we already fully downloaded the file. // we already fully downloaded the file.
if ( if (fileInfo && fileInfo.completed) {
cost === 0 || // If written_bytes is false that means the user has deleted/moved the
(fileInfo && (fileInfo.completed || fileInfo.download_directory)) // file manually on their file system, so we need to dispatch a
) { // doLoadVideo action to reconstruct the file from the blobs
return dispatch(doLoadVideo(uri)); 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) { 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 })); return dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri }));

View file

@ -6,5 +6,7 @@ export const NEW_USER_ACKNOWLEDGED = "welcome_acknowledged";
export const LANGUAGE = "language"; export const LANGUAGE = "language";
export const SHOW_NSFW = "showNsfw"; export const SHOW_NSFW = "showNsfw";
export const SHOW_UNAVAILABLE = "showUnavailable"; export const SHOW_UNAVAILABLE = "showUnavailable";
export const INSTANT_PURCHASE_ENABLED = "instantPurchaseEnabled";
export const INSTANT_PURCHASE_MAX = "instantPurchaseMax";
export const THEME = "theme"; export const THEME = "theme";
export const THEMES = "themes"; export const THEMES = "themes";

View file

@ -41,6 +41,9 @@ let lbry = {
language: "en", language: "en",
theme: "light", theme: "light",
themes: [], themes: [],
instantPurchaseMax: null,
instantPurchaseEnabled: false,
instantPurchaseMax: { currency: "LBC", amount: 0.1 },
}, },
}; };

View file

@ -21,6 +21,13 @@ const select = state => ({
daemonSettings: selectDaemonSettings(state), daemonSettings: selectDaemonSettings(state),
showNsfw: makeSelectClientSetting(settings.SHOW_NSFW)(state), showNsfw: makeSelectClientSetting(settings.SHOW_NSFW)(state),
showUnavailable: makeSelectClientSetting(settings.SHOW_UNAVAILABLE)(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), theme: makeSelectClientSetting(settings.THEME)(state),
themes: makeSelectClientSetting(settings.THEMES)(state), themes: makeSelectClientSetting(settings.THEMES)(state),
language: selectCurrentLanguage(state), language: selectCurrentLanguage(state),

View file

@ -13,6 +13,8 @@ class SettingsPage extends React.PureComponent {
super(props); super(props);
this.state = { this.state = {
instantPurchaseEnabled: props.instantPurchaseEnabled,
instantPurchaseMax: props.instantPurchaseMax,
clearingCache: false, clearingCache: false,
}; };
} }
@ -59,6 +61,22 @@ class SettingsPage extends React.PureComponent {
this.props.setClientSetting(settings.THEME, value); 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) { // onMaxUploadPrefChange(isLimited) {
// if (!isLimited) { // if (!isLimited) {
// this.setDaemonSetting("max_upload", 0.0); // this.setDaemonSetting("max_upload", 0.0);
@ -113,6 +131,8 @@ class SettingsPage extends React.PureComponent {
language, language,
languages, languages,
showNsfw, showNsfw,
instantPurchaseEnabled,
instantPurchaseMax,
showUnavailable, showUnavailable,
theme, theme,
themes, themes,
@ -167,7 +187,7 @@ class SettingsPage extends React.PureComponent {
</section> </section>
<section className="card"> <section className="card">
<div className="card__content"> <div className="card__content">
<h3>{__("Max Purchase Price")}</h3> <h3>{__("Purchase Settings")}</h3>
</div> </div>
<div className="card__content"> <div className="card__content">
<FormRow <FormRow
@ -211,6 +231,42 @@ class SettingsPage extends React.PureComponent {
)} )}
</div> </div>
</div> </div>
<div className="card__content">
<FormField
type="radio"
name="instant_purchase_max"
checked={!this.state.instantPurchaseEnabled}
label={__("Ask for confirmation of all purchases")}
onClick={e => {
this.oninstantPurchaseEnabledChange(false);
}}
/>
<div className="form-row">
<FormField
type="radio"
name="instant_purchase_max"
checked={this.state.instantPurchaseEnabled}
label={
"Single-click purchasing of content less than" +
(this.state.instantPurchaseEnabled ? "" : "...")
}
onClick={e => {
this.oninstantPurchaseEnabledChange(true);
}}
/>
{this.state.instantPurchaseEnabled &&
<FormFieldPrice
min="0.1"
step="0.1"
onChange={val => this.onInstantPurchaseMaxChange(val)}
defaultValue={this.state.instantPurchaseMax}
/>}
</div>
<div className="form-field__helper">
When this option is chosen, LBRY won't ask you to confirm
downloads below the given price.
</div>
</div>
</section> </section>
<section className="card"> <section className="card">

View file

@ -6,6 +6,10 @@ import lbry from "lbry";
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
clientSettings: { clientSettings: {
instantPurchaseEnabled: lbry.getClientSetting(
settings.INSTANT_PURCHASE_ENABLED
),
instantPurchaseMax: lbry.getClientSetting(settings.INSTANT_PURCHASE_MAX),
showNsfw: lbry.getClientSetting(settings.SHOW_NSFW), showNsfw: lbry.getClientSetting(settings.SHOW_NSFW),
showUnavailable: lbry.getClientSetting(settings.SHOW_UNAVAILABLE), showUnavailable: lbry.getClientSetting(settings.SHOW_UNAVAILABLE),
welcome_acknowledged: lbry.getClientSetting(settings.NEW_USER_ACKNOWLEDGED), welcome_acknowledged: lbry.getClientSetting(settings.NEW_USER_ACKNOWLEDGED),

View file

@ -28,6 +28,14 @@ export const selectSettingsIsGenerous = createSelector(
//refactor me //refactor me
export const selectShowNsfw = makeSelectClientSetting(settings.SHOW_NSFW); export const selectShowNsfw = makeSelectClientSetting(settings.SHOW_NSFW);
export const selectInstantPurchaseEnabled = makeSelectClientSetting(
settings.INSTANT_PURCHASE_ENABLED
);
export const selectInstantPurchaseMax = makeSelectClientSetting(
settings.INSTANT_PURCHASE_MAX
);
export const selectLanguages = createSelector( export const selectLanguages = createSelector(
_selectState, _selectState,
state => state.languages || {} state => state.languages || {}