From d82f4c43a2328e802d5234bb7d5013bffdeadde0 Mon Sep 17 00:00:00 2001 From: Alex Liebowitz Date: Tue, 19 Sep 2017 04:16:15 -0400 Subject: [PATCH 01/18] Show local file indicator on Channel page --- ui/js/page/channel/view.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/js/page/channel/view.jsx b/ui/js/page/channel/view.jsx index 617ef84ba..4917e65f0 100644 --- a/ui/js/page/channel/view.jsx +++ b/ui/js/page/channel/view.jsx @@ -53,6 +53,7 @@ class ChannelPage extends React.PureComponent { name: claim.name, claimId: claim.claim_id, })} + showLocal={true} /> ) : {__("No content found.")}; From 4ed7d2cba9e7f6b2f0fccdd22bdc11ae85bfdaf5 Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Thu, 21 Sep 2017 21:12:18 -0400 Subject: [PATCH 02/18] bad solution for #544 --- ui/js/selectors/claims.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js index 34c4ea688..c90b78843 100644 --- a/ui/js/selectors/claims.js +++ b/ui/js/selectors/claims.js @@ -180,7 +180,12 @@ export const selectMyChannelClaims = createSelector( const ids = state.myChannelClaims || []; const claims = []; - ids.forEach(id => claims.push(byId[id])); + ids.forEach(id => { + if (byId[id]) { + //I'm not sure why this check is necessary, but it ought to be a quick fix for https://github.com/lbryio/lbry-app/issues/544 + claims.push(byId[id]); + } + }); return claims; } From bbfb0fea674a4cc192d596c601c49191eb0d9cd1 Mon Sep 17 00:00:00 2001 From: Alex Liebowitz Date: Tue, 12 Sep 2017 00:24:04 -0400 Subject: [PATCH 03/18] Add component This is a presentational component for horizontal banks of icons (and sometimes other stuff). Convert and to use Factor out "downloaded/published" icon into its own component Add Featured Content icon to file tiles Add alignment style for Featured Content icon Update CHANGELOG.md Handle featured content icon adjustment in _icons.scss Remove IconSet component Converted back to a presentational span and move positioning styles into card SCSS Fix spacing between price and icons in file tiles and cards Before, it would be missing the space if there was a "local file" icon but no featured content icon. Rewrite Icon component to use semantic icons Now you can pass in a constant that indicates the meaning of the icon within the interface, e.g. icons.FEATURED instead of "icon-rocket". Also adds the appropriate title. All icons should eventually be updated to use this. Remove IconFeatured and IconLocal in favor of new Icon component Add space between Featured and Local icons In the conversion to a generic Icon component, we lost the styles that added padding on the left for FeaturedIcon and LocalIcon. Fix how rocket icon position is adjusted Before, it was using a negative margin, which altered the shape of the bounding box. Changed to use relative positioning. Add support for styling LBRY icons Move style for Featured icon into icons.js Uses new class name format for LBRY icon styles --- CHANGELOG.md | 2 +- ui/js/component/fileCard/view.jsx | 14 ++++---- ui/js/component/fileList/view.jsx | 3 +- ui/js/component/fileTile/view.jsx | 21 +++++++++--- ui/js/component/icon/index.js | 5 +++ ui/js/component/icon/view.jsx | 47 +++++++++++++++++++++++++++ ui/js/component/iconFeatured/index.js | 5 --- ui/js/component/iconFeatured/view.jsx | 15 --------- ui/js/constants/icons.js | 2 ++ ui/js/page/file/view.jsx | 36 ++++++++++++++++++-- ui/scss/_icons.scss | 15 +++++++++ ui/scss/component/_card.scss | 9 ++--- ui/scss/component/_file-tile.scss | 6 ---- 13 files changed, 136 insertions(+), 44 deletions(-) create mode 100644 ui/js/component/icon/index.js create mode 100644 ui/js/component/icon/view.jsx delete mode 100644 ui/js/component/iconFeatured/index.js delete mode 100644 ui/js/component/iconFeatured/view.jsx create mode 100644 ui/js/constants/icons.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e4786a85..a03a2f2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ Web UI version numbers should always match the corresponding version of LBRY App * ### Fixed - * + * Improve layout (and implementation) of the icon panel in file tiles and cards * ### Deprecated diff --git a/ui/js/component/fileCard/view.jsx b/ui/js/component/fileCard/view.jsx index eb29a1b2e..3ff093ef0 100644 --- a/ui/js/component/fileCard/view.jsx +++ b/ui/js/component/fileCard/view.jsx @@ -2,12 +2,13 @@ import React from "react"; import lbryuri from "lbryuri.js"; import CardMedia from "component/cardMedia"; import Link from "component/link"; -import { TruncatedText, Icon } from "component/common"; -import IconFeatured from "component/iconFeatured"; +import { TruncatedText } from "component/common"; +import Icon from "component/icon"; import FilePrice from "component/filePrice"; import UriIndicator from "component/uriIndicator"; import NsfwOverlay from "component/nsfwOverlay"; import TruncatedMarkdown from "component/truncatedMarkdown"; +import * as icons from "constants/icons"; class FileCard extends React.PureComponent { constructor(props) { @@ -94,11 +95,12 @@ class FileCard extends React.PureComponent { {title}
- + - {isRewardContent && {" "}} - {fileInfo && - {" "}} + {" "} + {isRewardContent && } + {" "} + {fileInfo && }
diff --git a/ui/js/component/fileList/view.jsx b/ui/js/component/fileList/view.jsx index 8fad10cdc..97010a104 100644 --- a/ui/js/component/fileList/view.jsx +++ b/ui/js/component/fileList/view.jsx @@ -87,7 +87,8 @@ class FileList extends React.PureComponent { diff --git a/ui/js/component/fileTile/view.jsx b/ui/js/component/fileTile/view.jsx index 1abd44952..b243ea1d6 100644 --- a/ui/js/component/fileTile/view.jsx +++ b/ui/js/component/fileTile/view.jsx @@ -1,4 +1,5 @@ import React from "react"; +import * as icons from "constants/icons"; import lbryuri from "lbryuri.js"; import CardMedia from "component/cardMedia"; import FileActions from "component/fileActions"; @@ -6,12 +7,17 @@ import Link from "component/link"; import { TruncatedText } from "component/common.js"; import FilePrice from "component/filePrice"; import NsfwOverlay from "component/nsfwOverlay"; -import IconFeatured from "component/iconFeatured"; +import Icon from "component/icon"; class FileTile extends React.PureComponent { static SHOW_EMPTY_PUBLISH = "publish"; static SHOW_EMPTY_PENDING = "pending"; + static defaultProps = { + showPrice: true, + showLocal: true, + }; + constructor(props) { super(props); this.state = { @@ -59,8 +65,10 @@ class FileTile extends React.PureComponent { isResolvingUri, showEmpty, navigate, - hidePrice, + showPrice, + showLocal, rewardedContentClaimIds, + fileInfo, } = this.props; const uri = lbryuri.normalize(this.props.uri); @@ -111,8 +119,13 @@ class FileTile extends React.PureComponent {
- {!hidePrice ? : null} - {isRewardContent && } + + {showPrice && } + {" "} + {isRewardContent && } + {" "} + {showLocal && fileInfo && } +
{uri}

{title} diff --git a/ui/js/component/icon/index.js b/ui/js/component/icon/index.js new file mode 100644 index 000000000..72b01f640 --- /dev/null +++ b/ui/js/component/icon/index.js @@ -0,0 +1,5 @@ +import React from "react"; +import { connect } from "react-redux"; +import Icon from "./view"; + +export default connect(null, null)(Icon); diff --git a/ui/js/component/icon/view.jsx b/ui/js/component/icon/view.jsx new file mode 100644 index 000000000..a27db9acc --- /dev/null +++ b/ui/js/component/icon/view.jsx @@ -0,0 +1,47 @@ +import React from "react"; +import * as icons from "constants/icons"; + +export default class Icon extends React.PureComponent { + static propTypes = { + icon: React.PropTypes.string.isRequired, + fixed: React.PropTypes.bool, + }; + + static defaultProps = { + fixed: false, + }; + + getIconInfo() { + if (this.props.icon.startsWith("icon-")) { + // Old style where FA icon class is passed in directly + return { className: this.props.icon, title: "" }; + } + + switch (this.props.icon) { + case icons.FEATURED: + return { + className: "icon-rocket", + title: "Watch content with this icon to earn weekly rewards.", + }; + case icons.LOCAL: + return { + className: "icon-folder", + title: "You have a copy of this file.", + }; + default: + throw new Error(`Unknown icon type "${this.props.icon}"`); + } + } + + render() { + const { className, title } = this.getIconInfo(); + + const spanClassName = + "icon " + + ("lbry-icon-" + this.props.icon + " ") + + className + + (this.props.fixed ? " icon-fixed-width " : ""); + + return ; + } +} diff --git a/ui/js/component/iconFeatured/index.js b/ui/js/component/iconFeatured/index.js deleted file mode 100644 index 02ce8fc38..000000000 --- a/ui/js/component/iconFeatured/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import IconFeatured from "./view"; - -export default connect(null, null)(IconFeatured); diff --git a/ui/js/component/iconFeatured/view.jsx b/ui/js/component/iconFeatured/view.jsx deleted file mode 100644 index 651d82338..000000000 --- a/ui/js/component/iconFeatured/view.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; -import { Icon } from "component/common.js"; - -const IconFeatured = props => { - return ( - - - - ); -}; - -export default IconFeatured; diff --git a/ui/js/constants/icons.js b/ui/js/constants/icons.js new file mode 100644 index 000000000..0b8b70348 --- /dev/null +++ b/ui/js/constants/icons.js @@ -0,0 +1,2 @@ +export const FEATURED = "featured"; +export const LOCAL = "local"; diff --git a/ui/js/page/file/view.jsx b/ui/js/page/file/view.jsx index 35209ae18..8f3bb8168 100644 --- a/ui/js/page/file/view.jsx +++ b/ui/js/page/file/view.jsx @@ -6,8 +6,40 @@ import { Thumbnail } from "component/common"; import FilePrice from "component/filePrice"; import FileDetails from "component/fileDetails"; import UriIndicator from "component/uriIndicator"; -import IconFeatured from "component/iconFeatured"; +import Icon from "component/icon"; import WalletSendTip from "component/walletSendTip"; +import DateTime from "component/dateTime"; +import * as icons from "constants/icons"; + +const FormatItem = props => { + const { + publishedDate, + contentType, + claim: { height }, + metadata: { language, license }, + } = props; + + const mediaType = lbry.getMediaType(contentType); + + return ( + + + + + + + + + + + + + + + +
{__("Published on")}
{__("Content-Type")}{mediaType}
{__("Language")}{language}
{__("License")}{license}
+ ); +}; class FilePage extends React.PureComponent { componentDidMount() { @@ -77,7 +109,7 @@ class FilePage extends React.PureComponent { {!fileInfo || fileInfo.written_bytes <= 0 ? - {isRewardContent && {" "}} + {isRewardContent && {" "}} : null}

{title}

diff --git a/ui/scss/_icons.scss b/ui/scss/_icons.scss index 537531e19..43321a287 100644 --- a/ui/scss/_icons.scss +++ b/ui/scss/_icons.scss @@ -23,6 +23,21 @@ transform: translate(0, 0); } +/* Custom styles for LBRY icons */ + +.lbry-icon-featured { + color: orangered; +} + +/* Adjustments for icon size and alignment */ +.icon-rocket { + font-size: 0.95em; + position: relative; + top: -0.04em; + margin-left: 0.025em; + margin-right: 0.025em; +} + /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .icon-glass:before { diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index 0562b4040..03b515a4b 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -167,6 +167,11 @@ $font-size-subtext-multiple: 0.82; position: absolute; top: 36% } + +.card__indicators { + float: right; +} + .card--small { width: var(--card-small-width); overflow-x: hidden; @@ -269,10 +274,6 @@ $padding-right-card-hover-hack: 30px; right: 0; } -.card__icon-featured-content { - color: orangered; -} - /* if we keep doing things like this, we should add a real grid system, but I'm going to be a selective dick about it - Jeremy */ diff --git a/ui/scss/component/_file-tile.scss b/ui/scss/component/_file-tile.scss index 7341cc606..c35be3f9b 100644 --- a/ui/scss/component/_file-tile.scss +++ b/ui/scss/component/_file-tile.scss @@ -3,12 +3,6 @@ $height-file-tile: $spacing-vertical * 6; .file-tile__row { overflow: hidden; height: $height-file-tile; - .credit-amount { - float: right; - } - .icon-featured { - float: right; - } //also a hack .card__media { height: $height-file-tile; From 0a4e17af5426420c5db94a334dfa5d57f1ff2e58 Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 22 Sep 2017 17:46:11 -0400 Subject: [PATCH 04/18] minor design changes --- ui/js/component/icon/view.jsx | 31 ++++++++++++------------------- ui/js/constants/icons.js | 4 ++-- ui/scss/_icons.scss | 6 +----- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/ui/js/component/icon/view.jsx b/ui/js/component/icon/view.jsx index a27db9acc..5c23af877 100644 --- a/ui/js/component/icon/view.jsx +++ b/ui/js/component/icon/view.jsx @@ -11,36 +11,29 @@ export default class Icon extends React.PureComponent { fixed: false, }; - getIconInfo() { - if (this.props.icon.startsWith("icon-")) { - // Old style where FA icon class is passed in directly - return { className: this.props.icon, title: "" }; - } + getIconClass() { + const { icon } = this.props; + return icon.startsWith("icon-") ? icon : "icon-" + icon; + } + + getIconTitle() { switch (this.props.icon) { case icons.FEATURED: - return { - className: "icon-rocket", - title: "Watch content with this icon to earn weekly rewards.", - }; + return __("Watch this and earn rewards."); case icons.LOCAL: - return { - className: "icon-folder", - title: "You have a copy of this file.", - }; + return __("You have a copy of this file."); default: - throw new Error(`Unknown icon type "${this.props.icon}"`); + return ""; } } render() { - const { className, title } = this.getIconInfo(); + const className = this.getIconClass(), + title = this.getIconTitle(); const spanClassName = - "icon " + - ("lbry-icon-" + this.props.icon + " ") + - className + - (this.props.fixed ? " icon-fixed-width " : ""); + "icon " + className + (this.props.fixed ? " icon-fixed-width " : ""); return ; } diff --git a/ui/js/constants/icons.js b/ui/js/constants/icons.js index 0b8b70348..6de5babcb 100644 --- a/ui/js/constants/icons.js +++ b/ui/js/constants/icons.js @@ -1,2 +1,2 @@ -export const FEATURED = "featured"; -export const LOCAL = "local"; +export const FEATURED = "rocket"; +export const LOCAL = "folder"; diff --git a/ui/scss/_icons.scss b/ui/scss/_icons.scss index 43321a287..8fbf846b3 100644 --- a/ui/scss/_icons.scss +++ b/ui/scss/_icons.scss @@ -23,14 +23,10 @@ transform: translate(0, 0); } -/* Custom styles for LBRY icons */ - -.lbry-icon-featured { - color: orangered; -} /* Adjustments for icon size and alignment */ .icon-rocket { + color: orangered; font-size: 0.95em; position: relative; top: -0.04em; From 27448499dd95e93d2ca382698e502bc720bc45bb Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 22 Sep 2017 18:11:42 -0400 Subject: [PATCH 05/18] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a03a2f2df..ee6ca0528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Web UI version numbers should always match the corresponding version of LBRY App ### Fixed * Improve layout (and implementation) of the icon panel in file tiles and cards + * The folder icon representing a local download now shows up properly on Channel pages (#587) * ### Deprecated From 93b1ab8ac642a90e35b24744b62f459e365d77ed Mon Sep 17 00:00:00 2001 From: Alex Liebowitz Date: Fri, 8 Sep 2017 01:03:37 -0400 Subject: [PATCH 06/18] 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 --- CHANGELOG.md | 2 +- ui/js/actions/content.js | 56 ++++++++++++++++++++++++++-------- ui/js/constants/settings.js | 2 ++ ui/js/lbry.js | 3 ++ ui/js/page/settings/index.js | 7 +++++ ui/js/page/settings/view.jsx | 58 +++++++++++++++++++++++++++++++++++- ui/js/reducers/settings.js | 4 +++ ui/js/selectors/settings.js | 8 +++++ 8 files changed, 125 insertions(+), 15 deletions(-) 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..51370908a 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,7 +187,7 @@ 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), diff --git a/ui/js/selectors/settings.js b/ui/js/selectors/settings.js index 5bf7f7f74..4b403d2c9 100644 --- a/ui/js/selectors/settings.js +++ b/ui/js/selectors/settings.js @@ -28,6 +28,14 @@ export const selectSettingsIsGenerous = createSelector( //refactor me 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( _selectState, state => state.languages || {} From b24b6e2e8e4b7ab7750f82776cb2f5cb641201be Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 22 Sep 2017 18:31:23 -0400 Subject: [PATCH 07/18] add labels, remove unused selectors --- ui/js/page/settings/view.jsx | 12 +++++++++++- ui/js/selectors/settings.js | 8 -------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ui/js/page/settings/view.jsx b/ui/js/page/settings/view.jsx index 51370908a..186abbb5c 100644 --- a/ui/js/page/settings/view.jsx +++ b/ui/js/page/settings/view.jsx @@ -190,6 +190,11 @@ class SettingsPage extends React.PureComponent {

{__("Purchase Settings")}

+
+ +
- + +
+ state.languages || {} From 31cc95cf0eb894953a91bed0607bbd200bb52c1b Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 22 Sep 2017 18:59:28 -0400 Subject: [PATCH 08/18] simplify balance check now that we are in redux land --- ui/js/actions/app.js | 3 ++- ui/js/actions/wallet.js | 23 +++++++++++++++------ ui/js/component/app/index.js | 2 -- ui/js/component/app/view.jsx | 5 ----- ui/js/lbry.js | 39 ------------------------------------ 5 files changed, 19 insertions(+), 53 deletions(-) diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index 67fb76071..0fb877573 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -1,5 +1,4 @@ import * as types from "constants/action_types"; -import * as settings from "constants/settings"; import lbry from "lbry"; import { selectUpdateUrl, @@ -8,6 +7,7 @@ import { selectUpgradeFilename, } from "selectors/app"; import { doFetchDaemonSettings } from "actions/settings"; +import { doBalanceSubscribe } from "actions/wallet"; import { doAuthenticate } from "actions/user"; import { doFetchFileInfosAndPublishedClaims } from "actions/file_info"; @@ -178,6 +178,7 @@ export function doDaemonReady() { dispatch(doAuthenticate()); dispatch({ type: types.DAEMON_READY }); dispatch(doFetchDaemonSettings()); + dispatch(doBalanceSubscribe()); dispatch(doFetchFileInfosAndPublishedClaims()); }; } diff --git a/ui/js/actions/wallet.js b/ui/js/actions/wallet.js index 0e90266fb..e7b36597a 100644 --- a/ui/js/actions/wallet.js +++ b/ui/js/actions/wallet.js @@ -9,12 +9,23 @@ import { doOpenModal, doShowSnackBar } from "actions/app"; import { doNavigate } from "actions/navigation"; import * as modals from "constants/modal_types"; -export function doUpdateBalance(balance) { - return { - type: types.UPDATE_BALANCE, - data: { - balance: balance, - }, +export function doUpdateBalance() { + return function(dispatch, getState) { + lbry.wallet_balance().then(balance => { + return dispatch({ + type: types.UPDATE_BALANCE, + data: { + balance: balance, + }, + }); + }); + }; +} + +export function doBalanceSubscribe() { + return function(dispatch, getState) { + dispatch(doUpdateBalance()); + setInterval(() => dispatch(doUpdateBalance()), 5000); }; } diff --git a/ui/js/component/app/index.js b/ui/js/component/app/index.js index 574c5f862..06ff45f18 100644 --- a/ui/js/component/app/index.js +++ b/ui/js/component/app/index.js @@ -5,7 +5,6 @@ import { selectUser } from "selectors/user"; import { doCheckUpgradeAvailable, doAlertError } from "actions/app"; import { doRecordScroll } from "actions/navigation"; import { doFetchRewardedContent } from "actions/content"; -import { doUpdateBalance } from "actions/wallet"; import App from "./view"; const select = (state, props) => ({ @@ -16,7 +15,6 @@ const select = (state, props) => ({ const perform = dispatch => ({ alertError: errorList => dispatch(doAlertError(errorList)), checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()), - updateBalance: balance => dispatch(doUpdateBalance(balance)), fetchRewardedContent: () => dispatch(doFetchRewardedContent()), recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)), }); diff --git a/ui/js/component/app/view.jsx b/ui/js/component/app/view.jsx index 37bd05c72..b349fd2de 100644 --- a/ui/js/component/app/view.jsx +++ b/ui/js/component/app/view.jsx @@ -10,7 +10,6 @@ class App extends React.PureComponent { const { alertError, checkUpgradeAvailable, - updateBalance, fetchRewardedContent, } = this.props; @@ -22,10 +21,6 @@ class App extends React.PureComponent { checkUpgradeAvailable(); } - lbry.balanceSubscribe(balance => { - updateBalance(balance); - }); - fetchRewardedContent(); this.scrollListener = () => this.props.recordScroll(window.scrollY); diff --git a/ui/js/lbry.js b/ui/js/lbry.js index 32770bcf1..ac787b353 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -279,45 +279,6 @@ lbry.getMediaType = function(contentType, fileName) { } }; -lbry._subscribeIdCount = 0; -lbry._balanceSubscribeCallbacks = {}; -lbry._balanceSubscribeInterval = 5000; - -lbry._balanceUpdateInterval = null; -lbry._updateBalanceSubscribers = function() { - lbry.wallet_balance().then(function(balance) { - for (let callback of Object.values(lbry._balanceSubscribeCallbacks)) { - callback(balance); - } - }); - - if ( - !lbry._balanceUpdateInterval && - Object.keys(lbry._balanceSubscribeCallbacks).length - ) { - lbry._balanceUpdateInterval = setInterval(() => { - lbry._updateBalanceSubscribers(); - }, lbry._balanceSubscribeInterval); - } -}; - -lbry.balanceSubscribe = function(callback) { - const subscribeId = ++lbry._subscribeIdCount; - lbry._balanceSubscribeCallbacks[subscribeId] = callback; - lbry._updateBalanceSubscribers(); - return subscribeId; -}; - -lbry.balanceUnsubscribe = function(subscribeId) { - delete lbry._balanceSubscribeCallbacks[subscribeId]; - if ( - lbry._balanceUpdateInterval && - !Object.keys(lbry._balanceSubscribeCallbacks).length - ) { - clearInterval(lbry._balanceUpdateInterval); - } -}; - lbry.showMenuIfNeeded = function() { const showingMenu = sessionStorage.getItem("menuShown") || null; const chosenMenu = lbry.getClientSetting("showDeveloperMenu") From 4a23d13438f7318212eacc98f2a9b4f460d47eb0 Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 22 Sep 2017 19:02:50 -0400 Subject: [PATCH 09/18] remove unnecessary lbry.js functions --- ui/js/lbry.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/ui/js/lbry.js b/ui/js/lbry.js index ac787b353..2f1d2d8ad 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -371,25 +371,6 @@ lbry.claim_list_mine = function(params = {}) { }); }; -lbry.claim_abandon = function(params = {}) { - return new Promise((resolve, reject) => { - apiCall("claim_abandon", params, resolve, reject); - }); -}; - -lbry.block_show = function(params = {}) { - return new Promise((resolve, reject) => { - apiCall( - "block_show", - params, - block => { - resolve(block); - }, - reject - ); - }); -}; - lbry._resolveXhrs = {}; lbry.resolve = function(params = {}) { return new Promise((resolve, reject) => { From 5c8db79b69c866c91b2e65f6fc202ae87f5b59ef Mon Sep 17 00:00:00 2001 From: Jeremy Kauffman Date: Fri, 22 Sep 2017 19:10:38 -0400 Subject: [PATCH 10/18] remove "showDeveloperMenu" as a setting --- ui/js/lbry.js | 22 ---------------------- ui/js/main.js | 4 +--- ui/js/page/developer.js | 22 ---------------------- 3 files changed, 1 insertion(+), 47 deletions(-) diff --git a/ui/js/lbry.js b/ui/js/lbry.js index 2f1d2d8ad..9f9dc0e51 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -37,7 +37,6 @@ let lbry = { debug: false, useCustomLighthouseServers: false, customLighthouseServers: [], - showDeveloperMenu: false, language: "en", theme: "light", themes: [], @@ -230,9 +229,6 @@ lbry.publishDeprecated = function( lbry.getClientSetting = function(setting) { var localStorageVal = localStorage.getItem("setting_" + setting); - if (setting == "showDeveloperMenu") { - return true; - } return localStorageVal === null ? lbry.defaultClientSettings[setting] : JSON.parse(localStorageVal); @@ -242,13 +238,6 @@ lbry.setClientSetting = function(setting, value) { return localStorage.setItem("setting_" + setting, JSON.stringify(value)); }; -lbry.formatName = function(name) { - // Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes) - name = name.replace("/s+/g", "-"); - name = name.toLowerCase().replace(lbryuri.REGEXP_INVALID_URI, ""); - return name; -}; - lbry.imagePath = function(file) { return "img/" + file; }; @@ -279,17 +268,6 @@ lbry.getMediaType = function(contentType, fileName) { } }; -lbry.showMenuIfNeeded = function() { - const showingMenu = sessionStorage.getItem("menuShown") || null; - const chosenMenu = lbry.getClientSetting("showDeveloperMenu") - ? "developer" - : "normal"; - if (chosenMenu != showingMenu) { - menu.showMenubar(chosenMenu == "developer"); - } - sessionStorage.setItem("menuShown", chosenMenu); -}; - lbry.getAppVersionInfo = function() { return new Promise((resolve, reject) => { ipcRenderer.once("version-info-received", (event, versionInfo) => { diff --git a/ui/js/main.js b/ui/js/main.js index e8a3221a0..9c6059366 100644 --- a/ui/js/main.js +++ b/ui/js/main.js @@ -16,14 +16,12 @@ const { remote, ipcRenderer, shell } = require("electron"); const contextMenu = remote.require("./menu/context-menu"); const app = require("./app"); -lbry.showMenuIfNeeded(); - window.addEventListener("contextmenu", event => { contextMenu.showContextMenu( remote.getCurrentWindow(), event.x, event.y, - lbry.getClientSetting("showDeveloperMenu") + env === "development" ); event.preventDefault(); }); diff --git a/ui/js/page/developer.js b/ui/js/page/developer.js index 4b69df173..970a0d3e7 100644 --- a/ui/js/page/developer.js +++ b/ui/js/page/developer.js @@ -11,7 +11,6 @@ class DeveloperPage extends React.PureComponent { super(props); this.state = { - showDeveloperMenu: lbry.getClientSetting("showDeveloperMenu"), useCustomLighthouseServers: lbry.getClientSetting( "useCustomLighthouseServers" ), @@ -22,14 +21,6 @@ class DeveloperPage extends React.PureComponent { }; } - handleShowDeveloperMenuChange(event) { - lbry.setClientSetting("showDeveloperMenu", event.target.checked); - lbry.showMenuIfNeeded(); - this.setState({ - showDeveloperMenu: event.target.checked, - }); - } - handleUseCustomLighthouseServersChange(event) { lbry.setClientSetting("useCustomLighthouseServers", event.target.checked); this.setState({ @@ -71,19 +62,6 @@ class DeveloperPage extends React.PureComponent {

{__("Developer Settings")}

-
- -