diff --git a/src/renderer/component/common/file-selector.jsx b/src/renderer/component/common/file-selector.jsx new file mode 100644 index 000000000..0858b488d --- /dev/null +++ b/src/renderer/component/common/file-selector.jsx @@ -0,0 +1,72 @@ +// @flow +import React from 'react'; +import { remote } from 'electron'; +import Button from 'component/link'; +import { FormRow } from 'component/common/form'; + +type Props = { + type: string, + currentPath: string, + onFileChosen: string => void, +}; + +class FileSelector extends React.PureComponent { + static defaultProps = { + type: 'file', + }; + + constructor() { + super(); + this.input = null; + } + + handleButtonClick() { + remote.dialog.showOpenDialog( + { + properties: + this.props.type === 'file' ? ['openFile'] : ['openDirectory', 'createDirectory'], + }, + paths => { + if (!paths) { + // User hit cancel, so do nothing + return; + } + + const path = paths[0]; + if (this.props.onFileChosen) { + this.props.onFileChosen(path); + } + } + ); + } + + input: ?HTMLInputElement; + + render() { + const { type, currentPath } = this.props; + + return ( + + {' '} - - { - this._inputElem = input; - }} - onFocus={() => { - this._inputElem.select(); - }} - readOnly="readonly" - value={this.state.path || __('No File Chosen')} - /> - - - ); - } -} - -export default FileSelector; diff --git a/src/renderer/component/fileActions/view.jsx b/src/renderer/component/fileActions/view.jsx index 1d1dcbf71..fef714b4b 100644 --- a/src/renderer/component/fileActions/view.jsx +++ b/src/renderer/component/fileActions/view.jsx @@ -22,9 +22,8 @@ class FileActions extends React.PureComponent { const { fileInfo, uri, openModal, claimIsMine, vertical } = this.props; const claimId = fileInfo ? fileInfo.claim_id : ''; - // showDelete = fileInfo && Object.keys(fileInfo).length > 0; + const showDelete = fileInfo && Object.keys(fileInfo).length > 0; - const showDelete = true; return (
diff --git a/src/renderer/component/fileCard/view.jsx b/src/renderer/component/fileCard/view.jsx index b7a72bb99..a30bbdacb 100644 --- a/src/renderer/component/fileCard/view.jsx +++ b/src/renderer/component/fileCard/view.jsx @@ -95,7 +95,7 @@ class FileCard extends React.PureComponent { - {obscureNsfw && } + {shouldObscureNsfw && }
); /* eslint-enable jsx-a11y/click-events-have-key-events */ diff --git a/src/renderer/component/formField/view.jsx b/src/renderer/component/formField/view.jsx index 0ce16256f..9d8dca9ec 100644 --- a/src/renderer/component/formField/view.jsx +++ b/src/renderer/component/formField/view.jsx @@ -2,7 +2,7 @@ /* eslint-disable */ import React from 'react'; import PropTypes from 'prop-types'; -import FileSelector from 'component/file-selector.js'; +import FileSelector from 'component/common/file-selector'; import SimpleMDE from 'react-simplemde-editor'; import { formFieldNestedLabelTypes, formFieldId } from 'component/common/form'; import style from 'react-simplemde-editor/dist/simplemde.min.css'; diff --git a/src/renderer/component/formFieldPrice/view.jsx b/src/renderer/component/formFieldPrice/view.jsx index 71382bb47..405e9b8ce 100644 --- a/src/renderer/component/formFieldPrice/view.jsx +++ b/src/renderer/component/formFieldPrice/view.jsx @@ -1,63 +1,5 @@ -import React from 'react'; -import FormField from 'component/formField'; +// This just exists so the app builds. It will be removed -class FormFieldPrice extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - amount: props.defaultValue && props.defaultValue.amount ? props.defaultValue.amount : '', - currency: - props.defaultValue && props.defaultValue.currency ? props.defaultValue.currency : 'LBC', - }; - } - - handleChange(newValues) { - const newState = Object.assign({}, this.state, newValues); - this.setState(newState); - this.props.onChange({ - amount: newState.amount, - currency: newState.currency, - }); - } - - handleFeeAmountChange(event) { - this.handleChange({ - amount: event.target.value ? Number(event.target.value) : null, - }); - } - - handleFeeCurrencyChange(event) { - this.handleChange({ currency: event.target.value }); - } - - render() { - const { defaultValue, placeholder, min } = this.props; - - return ( - - this.handleFeeAmountChange(event)} - defaultValue={defaultValue && defaultValue.amount ? defaultValue.amount : ''} - className="form-field__input--inline" - /> - this.handleFeeCurrencyChange(event)} - defaultValue={defaultValue && defaultValue.currency ? defaultValue.currency : ''} - className="form-field__input--inline" - > - - - - - ); - } -} +const FormFieldPrice = () => null; export default FormFieldPrice; diff --git a/src/renderer/component/nsfwOverlay/view.jsx b/src/renderer/component/nsfwOverlay/view.jsx index c89fe6da9..c3ab332dd 100644 --- a/src/renderer/component/nsfwOverlay/view.jsx +++ b/src/renderer/component/nsfwOverlay/view.jsx @@ -1,15 +1,11 @@ import React from 'react'; -import Link from 'component/link'; +import Button from 'component/link'; -const NsfwOverlay = props => ( +const NsfwOverlay = () => (

{__('This content is Not Safe For Work. To view adult content, please change your')}{' '} - props.navigateSettings()} - label={__('Settings')} - />. +

); diff --git a/src/renderer/component/walletSend/view.jsx b/src/renderer/component/walletSend/view.jsx index dce943b8a..ec1114202 100644 --- a/src/renderer/component/walletSend/view.jsx +++ b/src/renderer/component/walletSend/view.jsx @@ -40,15 +40,7 @@ class WalletSend extends React.PureComponent { }} onSubmit={this.handleSubmit} validate={validateSendTx} - render={({ - values, - errors, - touched, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - }) => ( + render={({ values, errors, touched, handleChange, handleBlur, handleSubmit }) => (
{ error={!!values.amount && touched.amount && errors.amount} render={() => ( { input: ?HTMLInputElement; throttledGetSearchSuggestions: string => void; + render() { const { searchQuery, isActive, address, suggestions } = this.props; diff --git a/src/renderer/page/settings/index.js b/src/renderer/page/settings/index.js index ca277df82..e4f4ff930 100644 --- a/src/renderer/page/settings/index.js +++ b/src/renderer/page/settings/index.js @@ -1,4 +1,3 @@ -import React from 'react'; import { connect } from 'react-redux'; import * as settings from 'constants/settings'; import { doClearCache } from 'redux/actions/app'; @@ -6,7 +5,6 @@ import { doSetDaemonSetting, doSetClientSetting, doGetThemes, - doSetTheme, doChangeLanguage, } from 'redux/actions/settings'; import { @@ -23,8 +21,7 @@ const select = 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), + currentTheme: makeSelectClientSetting(settings.THEME)(state), themes: makeSelectClientSetting(settings.THEMES)(state), language: selectCurrentLanguage(state), languages: selectLanguages(state), diff --git a/src/renderer/page/settings/view.jsx b/src/renderer/page/settings/view.jsx index 84c7a80bf..78960140c 100644 --- a/src/renderer/page/settings/view.jsx +++ b/src/renderer/page/settings/view.jsx @@ -1,19 +1,112 @@ -import React from 'react'; -import FormField from 'component/formField'; -import { FormRow } from 'component/common/form'; +// @flow +import * as React from 'react'; +import { FormField, FormFieldPrice } from 'component/common/form'; import * as settings from 'constants/settings'; -import lbry from 'lbry.js'; -import Link from 'component/link'; -import FormFieldPrice from 'component/formFieldPrice'; +import Button from 'component/link'; import Page from 'component/page'; +import FileSelector from 'component/common/file-selector'; -class SettingsPage extends React.PureComponent { - constructor(props) { +export type Price = { + currency: string, + amount: number, +}; + +type DaemonSettings = { + download_directory: string, + disable_max_key_fee: boolean, + share_usage_data: boolean, +}; + +type Props = { + setDaemonSetting: (string, boolean | string | Price) => void, + setClientSetting: (string, boolean | string | Price) => void, + clearCache: () => Promise, + getThemes: () => void, + daemonSettings: DaemonSettings, + showNsfw: boolean, + instantPurchaseEnabled: boolean, + instantPurchaseMax: Price, + showUnavailable: boolean, + currentTheme: string, + themes: Array, + automaticDarkModeEnabled: boolean, +}; + +type State = { + clearingCache: boolean, +}; + +class SettingsPage extends React.PureComponent { + constructor(props: Props) { super(props); this.state = { clearingCache: false, }; + + (this: any).onDownloadDirChange = this.onDownloadDirChange.bind(this); + (this: any).onKeyFeeChange = this.onKeyFeeChange.bind(this); + (this: any).onInstantPurchaseMaxChange = this.onInstantPurchaseMaxChange.bind(this); + (this: any).onShowNsfwChange = this.onShowNsfwChange.bind(this); + (this: any).onShowUnavailableChange = this.onShowUnavailableChange.bind(this); + (this: any).onShareDataChange = this.onShareDataChange.bind(this); + (this: any).onThemeChange = this.onThemeChange.bind(this); + (this: any).onAutomaticDarkModeChange = this.onAutomaticDarkModeChange.bind(this); + (this: any).clearCache = this.clearCache.bind(this); + // (this: any).onLanguageChange = this.onLanguageChange.bind(this) + } + + componentDidMount() { + this.props.getThemes(); + } + + onRunOnStartChange(event: SyntheticInputEvent<*>) { + this.setDaemonSetting('run_on_startup', event.target.checked); + } + + onShareDataChange(event: SyntheticInputEvent<*>) { + this.setDaemonSetting('share_usage_data', event.target.checked); + } + + onDownloadDirChange(newDirectory: string) { + this.setDaemonSetting('download_directory', newDirectory); + } + + onKeyFeeChange(newValue: Price) { + this.setDaemonSetting('max_key_fee', newValue); + } + + onKeyFeeDisableChange(isDisabled: boolean) { + this.setDaemonSetting('disable_max_key_fee', isDisabled); + } + + onThemeChange(event: SyntheticInputEvent<*>) { + const { value } = event.target; + this.props.setClientSetting(settings.THEME, value); + } + + onAutomaticDarkModeChange(event: SyntheticInputEvent<*>) { + this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, event.target.checked); + } + + onInstantPurchaseEnabledChange(enabled: boolean) { + this.props.setClientSetting(settings.INSTANT_PURCHASE_ENABLED, enabled); + } + + onInstantPurchaseMaxChange(newValue: Price) { + this.props.setClientSetting(settings.INSTANT_PURCHASE_MAX, newValue); + } + + onShowNsfwChange(event: SyntheticInputEvent<*>) { + this.props.setClientSetting(settings.SHOW_NSFW, event.target.checked); + } + + onShowUnavailableChange(event: SyntheticInputEvent<*>) { + this.props.setClientSetting(settings.SHOW_UNAVAILABLE, event.target.checked); + } + + setDaemonSetting(name: string, value: boolean | string | Price) { + this.props.setDaemonSetting(name, value); } clearCache() { @@ -24,329 +117,206 @@ class SettingsPage extends React.PureComponent { this.setState({ clearingCache: false }); window.location.href = 'index.html'; }; - const clear = () => this.props.clearCache().then(success.bind(this)); + const clear = () => this.props.clearCache().then(success); setTimeout(clear, 1000, { once: true }); } - setDaemonSetting(name, value) { - this.props.setDaemonSetting(name, value); - } - - onRunOnStartChange(event) { - this.setDaemonSetting('run_on_startup', event.target.checked); - } - - onShareDataChange(event) { - this.setDaemonSetting('share_usage_data', event.target.checked); - } - - onDownloadDirChange(event) { - this.setDaemonSetting('download_directory', event.target.value); - } - - onKeyFeeChange(newValue) { - const setting = newValue; - - // this is stupid and should be fixed... somewhere - if (setting && (setting.amount === undefined || setting.amount === null)) { - setting.amount = 0; - } - - this.setDaemonSetting('max_key_fee', setting); - } - - onKeyFeeDisableChange(isDisabled) { - this.setDaemonSetting('disable_max_key_fee', isDisabled); - } - - onThemeChange(event) { - const { value } = event.target; - this.props.setClientSetting(settings.THEME, value); - } - - onAutomaticDarkModeChange(event) { - this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, event.target.checked); - } - - onInstantPurchaseEnabledChange(enabled) { - this.props.setClientSetting(settings.INSTANT_PURCHASE_ENABLED, enabled); - } - - onInstantPurchaseMaxChange(newValue) { - this.props.setClientSetting(settings.INSTANT_PURCHASE_MAX, newValue); - } - - // onMaxUploadPrefChange(isLimited) { - // if (!isLimited) { - // this.setDaemonSetting("max_upload", 0.0); - // } - // this.setState({ - // isMaxUpload: isLimited, - // }); - // } - // - // onMaxUploadFieldChange(event) { - // this.setDaemonSetting("max_upload", Number(event.target.value)); - // } - // - // onMaxDownloadPrefChange(isLimited) { - // if (!isLimited) { - // this.setDaemonSetting("max_download", 0.0); - // } - // this.setState({ - // isMaxDownload: isLimited, - // }); - // } - // - // onMaxDownloadFieldChange(event) { - // this.setDaemonSetting("max_download", Number(event.target.value)); - // } - - onShowNsfwChange(event) { - this.props.setClientSetting(settings.SHOW_NSFW, event.target.checked); - } - - onLanguageChange(e) { - this.props.changeLanguage(e.target.value); - this.forceUpdate(); - } - - onShowUnavailableChange(event) { - this.props.setClientSetting(settings.SHOW_UNAVAILABLE, event.target.checked); - } - - componentWillMount() { - this.props.getThemes(); - } - - componentDidMount() {} - render() { const { daemonSettings, - language, - languages, showNsfw, instantPurchaseEnabled, instantPurchaseMax, showUnavailable, - theme, + currentTheme, themes, automaticDarkModeEnabled, } = this.props; - if (!daemonSettings || Object.keys(daemonSettings).length === 0) { - return ( -
- {__('Failed to load settings.')} -
- ); - } + const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0; + return ( - {/* -
-
-

{__("Language")}

-
-
-
- - - {Object.keys(languages).map(dLang => - - )} - -
-
-
*/} -
-
-

{__('Download Directory')}

-
-
- -
-
-
-
-

{__('Max Purchase Price')}

-
-
- { - this.onKeyFeeDisableChange(true); - }} - defaultChecked={daemonSettings.disable_max_key_fee} - label={__('No Limit')} - /> -
- { - this.onKeyFeeDisableChange(false); - }} - defaultChecked={!daemonSettings.disable_max_key_fee} - label={daemonSettings.disable_max_key_fee ? __('Choose limit') : __('Limit to')} + {noDaemonSettings ? ( +
+
{__('Failed to load settings.')}
+
+ ) : ( + +
+
{__('Download Directory')}
+ {__('LBRY downloads will be saved here.')} + - {!daemonSettings.disable_max_key_fee && ( +
+
+
{__('Max Purchase Price')}
+ + {__( + 'This will prevent you from purchasing any content over a certain cost, as a safety measure.' + )} + +
+ { + this.onKeyFeeDisableChange(true); + }} + /> + { + this.onKeyFeeDisableChange(false); + }} + checked={!daemonSettings.disable_max_key_fee} + postfix={__('Choose limit')} + /> - )} -
-
- {__( - 'This will prevent you from purchasing any content over this cost, as a safety measure.' - )} -
-
-
+ + -
-
-

{__('Purchase Confirmations')}

-
-
- { - this.onInstantPurchaseEnabledChange(false); - }} - /> -
- { - this.onInstantPurchaseEnabledChange(true); - }} - /> - {instantPurchaseEnabled && ( - this.onInstantPurchaseMaxChange(val)} - defaultValue={instantPurchaseMax} +
+
{__('Purchase Confirmations')}
+
+ {__( + "When this option is chosen, LBRY won't ask you to confirm downloads below your chosen price." + )} +
+
+ { + this.onInstantPurchaseEnabledChange(false); + }} /> - )} -
-
- When this option is chosen, LBRY won't ask you to confirm downloads below the given - price. -
-
-
-
-
-

{__('Content')}

-
-
- - -
-
+ { + this.onInstantPurchaseEnabledChange(true); + }} + /> + + + -
-
-

{__('Share Diagnostic Data')}

-
-
- -
-
+
+
{__('Content Settings')}
+
+ + +
+
-
-
-

{__('Theme')}

-
-
- - {themes.map((theme, index) => ( - - ))} - +
+
{__('Share Diagnostic Data')}
+
{__('List what we are doing with the data here')}
+
+ +
+
- -
-
+
+
{__('Theme')}
-
-
-

{__('Application Cache')}

-
-
-

- ( + + )} /> -

-
-
+ + +
+ +
+
{__('Application Cache')}
+ + {__('This will delete your subscriptions, ... other stuff')} + +
+
+
+ + )}
); } diff --git a/src/renderer/scss/_gui.scss b/src/renderer/scss/_gui.scss index d76e75b96..4a8d92c9e 100644 --- a/src/renderer/scss/_gui.scss +++ b/src/renderer/scss/_gui.scss @@ -94,22 +94,39 @@ ul { } input { - cursor: pointer; border-bottom: var(--input-border-size) solid var(--input-border-color); color: var(--input-color); line-height: 1; + cursor: text; - &[type='text'] { - cursor: text; + &[type='radio'], + &[type='checkbox'], + &[type='file'], + &[type='select'] { + cursor: pointer; + } + + &[type='file'] { + border-bottom: none; } &.input-copyable { background: var(--input-bg); color: var(--input-disabled-color); - width: 100%; font-family: 'Consolas', 'Lucida Console', 'Adobe Source Code Pro', monospace; border-bottom: 1px dotted var(--color-divider); + width: 100%; + flex: 1; } + + &:disabled { + color: var(--input-disabled-color); + border-bottom: var(--input-border-size) solid var(--input-disabled-color); + } +} + +button + input { + margin-left: $spacing-vertical * 2/3; } dl { diff --git a/src/renderer/scss/all.scss b/src/renderer/scss/all.scss index 541c9ecd1..89a8fc12e 100644 --- a/src/renderer/scss/all.scss +++ b/src/renderer/scss/all.scss @@ -6,7 +6,6 @@ @import 'component/_button.scss'; @import 'component/_card.scss'; @import 'component/_file-download.scss'; -@import 'component/_file-selector.scss'; @import 'component/_file-tile.scss'; @import 'component/_form-field.scss'; @import 'component/_header.scss'; diff --git a/src/renderer/scss/component/_button.scss b/src/renderer/scss/component/_button.scss index 3a20ca105..7e0336834 100644 --- a/src/renderer/scss/component/_button.scss +++ b/src/renderer/scss/component/_button.scss @@ -1,4 +1,4 @@ -// This will go away +// This will be moved and updated once we figure out how it should look // It's for the download progress "button" .faux-button-block { display: inline-block; @@ -9,6 +9,8 @@ text-align: center; border-radius: var(--button-radius); text-transform: uppercase; + color: var(--color-white); + .icon { top: 0em; } diff --git a/src/renderer/scss/component/_card.scss b/src/renderer/scss/component/_card.scss index fbdc43dac..5bf0778d7 100644 --- a/src/renderer/scss/component/_card.scss +++ b/src/renderer/scss/component/_card.scss @@ -69,7 +69,7 @@ .card__title { font-size: 1.5em; font-weight: 800; - padding: $spacing-vertical / 3 0; + padding: 0; } .card__title--small { @@ -88,10 +88,20 @@ padding-top: 0; } -.card-media__internal-links { - position: absolute; - top: $spacing-vertical * 2/3; - right: $spacing-vertical * 2/3; +// .card-media__internal__links should always be inside a card +.card { + .card-media__internal-links { + position: absolute; + top: $spacing-vertical * 2/3; + right: $spacing-vertical * 2/3; + } +} + +.card--small { + .card-media__internal-links { + top: $spacing-vertical * 1/3; + right: $spacing-vertical * 1/3; + } } // Channel info with buttons on the right side @@ -107,7 +117,6 @@ .card__content { margin-top: var(--card-margin); - margin-bottom: var(--card-margin); } .card__subtext-title { diff --git a/src/renderer/scss/component/_file-selector.scss b/src/renderer/scss/component/_file-selector.scss deleted file mode 100644 index 8172da203..000000000 --- a/src/renderer/scss/component/_file-selector.scss +++ /dev/null @@ -1,23 +0,0 @@ -.form-field--file, -.form-field--directory { - width: 100%; -} - -.file-selector { - display: flex; -} - -.file-selector__choose-button { - font-family: inherit; - line-height: 0; - color: inherit; - margin-right: 16px; -} - -.file-selector__path { - font-size: 14px; - flex-grow: 2; - .input-copyable { - width: 100%; - } -} diff --git a/src/renderer/scss/component/_form-field.scss b/src/renderer/scss/component/_form-field.scss index 5a1ad7406..ff2e743a1 100644 --- a/src/renderer/scss/component/_form-field.scss +++ b/src/renderer/scss/component/_form-field.scss @@ -1,27 +1,37 @@ .form-row { display: flex; flex-direction: row; + align-items: flex-end; .form-field:not(:first-of-type) { padding-left: $spacing-vertical; } &.form-row--padded { - padding: $spacing-vertical * 2/3 0; + padding-top: $spacing-vertical * 2/3; + } + + &.form-row--centered { + align-items: center; } } -.form-field__wrapper { +.form-field__input { display: flex; - padding: $spacing-vertical / 3 0; + padding-top: $spacing-vertical / 3; - // Hmm, not sure about this - // without this the checkbox isn't on the same line as other form-field text - input[type='checkbox'] { + input[type='checkbox'], + input[type='radio'] { margin-top: 5px; } } +.form-field { + label { + cursor: pointer; + } +} + .form-field__error { color: var(--color-error); } @@ -30,18 +40,24 @@ color: var(--color-grey-dark); } +.form-field__help { + color: var(--color-grey-dark); + font-size: 0.8em; + padding-top: $spacing-vertical * 1/3; +} + .form-field__prefix { - padding-right: 5px; + padding-right: $spacing-vertical * 1/3; } .form-field__postfix { - padding-left: 5px; + padding-left: $spacing-vertical * 1/3; } // Not sure if I like these // Maybe this should be in gui.scss? -.input--lbc-amount { - width: 75px; +.input--price-amount { + width: 50px; font-weight: 700; } diff --git a/src/renderer/scss/component/_nav.scss b/src/renderer/scss/component/_nav.scss index 4c793cea7..928d7a9f8 100644 --- a/src/renderer/scss/component/_nav.scss +++ b/src/renderer/scss/component/_nav.scss @@ -14,7 +14,7 @@ display: flex; justify-content: space-between; align-items: center; - padding: 5px; + padding: 0 5px; } .nav__actions-history {