521 lines
18 KiB
JavaScript
521 lines
18 KiB
JavaScript
// @flow
|
|
import * as React from 'react';
|
|
|
|
import { FormField, FormFieldPrice } from 'component/common/form';
|
|
import Button from 'component/button';
|
|
import I18nMessage from 'component/i18nMessage';
|
|
import Page from 'component/page';
|
|
import SettingWalletServer from 'component/settingWalletServer';
|
|
import SettingAutoLaunch from 'component/settingAutoLaunch';
|
|
import SettingClosingBehavior from 'component/settingClosingBehavior';
|
|
import FileSelector from 'component/common/file-selector';
|
|
import { SETTINGS } from 'lbry-redux';
|
|
import Card from 'component/common/card';
|
|
import { getPasswordFromCookie } from 'util/saved-passwords';
|
|
import Spinner from 'component/spinner';
|
|
|
|
// @if TARGET='app'
|
|
const IS_MAC = process.platform === 'darwin';
|
|
// @endif
|
|
|
|
type Price = {
|
|
currency: string,
|
|
amount: number,
|
|
};
|
|
|
|
type SetDaemonSettingArg = boolean | string | number | Price;
|
|
|
|
type DaemonSettings = {
|
|
download_dir: string,
|
|
share_usage_data: boolean,
|
|
max_key_fee?: Price,
|
|
max_connections_per_download?: number,
|
|
save_files: boolean,
|
|
save_blobs: boolean,
|
|
ffmpeg_path: string,
|
|
};
|
|
|
|
type Props = {
|
|
setDaemonSetting: (string, ?SetDaemonSettingArg) => void,
|
|
clearDaemonSetting: string => void,
|
|
setClientSetting: (string, SetDaemonSettingArg) => void,
|
|
daemonSettings: DaemonSettings,
|
|
isAuthenticated: boolean,
|
|
instantPurchaseEnabled: boolean,
|
|
instantPurchaseMax: Price,
|
|
encryptWallet: () => void,
|
|
decryptWallet: () => void,
|
|
updateWalletStatus: () => void,
|
|
walletEncrypted: boolean,
|
|
hideBalance: boolean,
|
|
confirmForgetPassword: ({}) => void,
|
|
ffmpegStatus: { available: boolean, which: string },
|
|
findingFFmpeg: boolean,
|
|
findFFmpeg: () => void,
|
|
language?: string,
|
|
syncEnabled: boolean,
|
|
enterSettings: () => void,
|
|
exitSettings: () => void,
|
|
};
|
|
|
|
type State = {
|
|
clearingCache: boolean,
|
|
storedPassword: boolean,
|
|
};
|
|
|
|
class SettingsPage extends React.PureComponent<Props, State> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
clearingCache: false,
|
|
storedPassword: false,
|
|
};
|
|
|
|
(this: any).onKeyFeeChange = this.onKeyFeeChange.bind(this);
|
|
(this: any).onMaxConnectionsChange = this.onMaxConnectionsChange.bind(this);
|
|
(this: any).onKeyFeeDisableChange = this.onKeyFeeDisableChange.bind(this);
|
|
(this: any).onInstantPurchaseMaxChange = this.onInstantPurchaseMaxChange.bind(this);
|
|
(this: any).onThemeChange = this.onThemeChange.bind(this);
|
|
(this: any).onAutomaticDarkModeChange = this.onAutomaticDarkModeChange.bind(this);
|
|
(this: any).onConfirmForgetPassword = this.onConfirmForgetPassword.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
const { isAuthenticated, ffmpegStatus, daemonSettings, findFFmpeg, enterSettings } = this.props;
|
|
|
|
// @if TARGET='app'
|
|
const { available } = ffmpegStatus;
|
|
const { ffmpeg_path: ffmpegPath } = daemonSettings;
|
|
if (!available) {
|
|
if (ffmpegPath) {
|
|
this.clearDaemonSetting('ffmpeg_path');
|
|
}
|
|
findFFmpeg();
|
|
}
|
|
// @endif
|
|
|
|
if (isAuthenticated || !IS_WEB) {
|
|
this.props.updateWalletStatus();
|
|
getPasswordFromCookie().then(p => {
|
|
if (typeof p === 'string') {
|
|
this.setState({ storedPassword: true });
|
|
}
|
|
});
|
|
}
|
|
enterSettings();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
const { exitSettings } = this.props;
|
|
exitSettings();
|
|
}
|
|
|
|
onFFmpegFolder(path: string) {
|
|
this.setDaemonSetting('ffmpeg_path', path);
|
|
this.findFFmpeg();
|
|
}
|
|
|
|
onKeyFeeChange(newValue: Price) {
|
|
this.setDaemonSetting('max_key_fee', newValue);
|
|
}
|
|
|
|
onMaxConnectionsChange(event: SyntheticInputEvent<*>) {
|
|
const { value } = event.target;
|
|
this.setDaemonSetting('max_connections_per_download', value);
|
|
}
|
|
|
|
onKeyFeeDisableChange(isDisabled: boolean) {
|
|
if (isDisabled) this.setDaemonSetting('max_key_fee');
|
|
}
|
|
|
|
onThemeChange(event: SyntheticInputEvent<*>) {
|
|
const { value } = event.target;
|
|
|
|
if (value === 'dark') {
|
|
this.onAutomaticDarkModeChange(false);
|
|
}
|
|
|
|
this.props.setClientSetting(SETTINGS.THEME, value);
|
|
}
|
|
|
|
onAutomaticDarkModeChange(value: boolean) {
|
|
this.props.setClientSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED, value);
|
|
}
|
|
|
|
onInstantPurchaseEnabledChange(enabled: boolean) {
|
|
this.props.setClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, enabled);
|
|
}
|
|
|
|
onInstantPurchaseMaxChange(newValue: Price) {
|
|
this.props.setClientSetting(SETTINGS.INSTANT_PURCHASE_MAX, newValue);
|
|
}
|
|
|
|
onChangeEncryptWallet() {
|
|
const { decryptWallet, walletEncrypted, encryptWallet } = this.props;
|
|
if (walletEncrypted) {
|
|
decryptWallet();
|
|
} else {
|
|
encryptWallet();
|
|
}
|
|
}
|
|
|
|
onConfirmForgetPassword() {
|
|
const { confirmForgetPassword } = this.props;
|
|
confirmForgetPassword({
|
|
callback: () => {
|
|
this.setState({ storedPassword: false });
|
|
},
|
|
});
|
|
}
|
|
|
|
setDaemonSetting(name: string, value: ?SetDaemonSettingArg): void {
|
|
this.props.setDaemonSetting(name, value);
|
|
}
|
|
|
|
clearDaemonSetting(name: string): void {
|
|
this.props.clearDaemonSetting(name);
|
|
}
|
|
|
|
findFFmpeg(): void {
|
|
this.props.findFFmpeg();
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
daemonSettings,
|
|
ffmpegStatus,
|
|
instantPurchaseEnabled,
|
|
instantPurchaseMax,
|
|
isAuthenticated,
|
|
walletEncrypted,
|
|
setDaemonSetting,
|
|
setClientSetting,
|
|
hideBalance,
|
|
findingFFmpeg,
|
|
language,
|
|
} = this.props;
|
|
|
|
const { storedPassword } = this.state;
|
|
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
|
const defaultMaxKeyFee = { currency: 'USD', amount: 50 };
|
|
const disableMaxKeyFee = !(daemonSettings && daemonSettings.max_key_fee);
|
|
const connectionOptions = [1, 2, 4, 6, 10, 20];
|
|
// @if TARGET='app'
|
|
const { available: ffmpegAvailable, which: ffmpegPath } = ffmpegStatus;
|
|
// @endif
|
|
|
|
return (
|
|
<Page
|
|
noFooter
|
|
noSideNavigation
|
|
backout={{
|
|
title: __('Advanced settings'),
|
|
backLabel: __('Done'),
|
|
}}
|
|
className="card-stack"
|
|
>
|
|
{!IS_WEB && noDaemonSettings ? (
|
|
<section className="card card--section">
|
|
<div className="card__title card__title--deprecated">{__('Failed to load settings.')}</div>
|
|
</section>
|
|
) : (
|
|
<div>
|
|
{/* @if TARGET='app' */}
|
|
<Card
|
|
title={__('Neetwork and data settings')}
|
|
actions={
|
|
<React.Fragment>
|
|
<FormField
|
|
type="checkbox"
|
|
name="save_files"
|
|
onChange={() => setDaemonSetting('save_files', !daemonSettings.save_files)}
|
|
checked={daemonSettings.save_files}
|
|
label={__('Save all viewed content to your downloads directory')}
|
|
helper={__(
|
|
'Paid content and some file types are saved by default. Changing this setting will not affect previously downloaded content.'
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
type="checkbox"
|
|
name="save_blobs"
|
|
onChange={() => setDaemonSetting('save_blobs', !daemonSettings.save_blobs)}
|
|
checked={daemonSettings.save_blobs}
|
|
label={__('Save hosting data to help the LBRY network')}
|
|
helper={
|
|
<React.Fragment>
|
|
{__("If disabled, LBRY will be very sad and you won't be helping improve the network.")}{' '}
|
|
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/host-content" />.
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
|
|
<Card
|
|
title={__('Max purchase price')}
|
|
actions={
|
|
<React.Fragment>
|
|
<FormField
|
|
type="radio"
|
|
name="no_max_purchase_no_limit"
|
|
checked={disableMaxKeyFee}
|
|
label={__('No Limit')}
|
|
onChange={() => {
|
|
this.onKeyFeeDisableChange(true);
|
|
}}
|
|
/>
|
|
<FormField
|
|
type="radio"
|
|
name="max_purchase_limit"
|
|
checked={!disableMaxKeyFee}
|
|
onChange={() => {
|
|
this.onKeyFeeDisableChange(false);
|
|
this.onKeyFeeChange(defaultMaxKeyFee);
|
|
}}
|
|
label={__('Choose limit')}
|
|
/>
|
|
|
|
{!disableMaxKeyFee && (
|
|
<FormFieldPrice
|
|
language={language}
|
|
name="max_key_fee"
|
|
min={0}
|
|
onChange={this.onKeyFeeChange}
|
|
price={daemonSettings.max_key_fee ? daemonSettings.max_key_fee : defaultMaxKeyFee}
|
|
/>
|
|
)}
|
|
|
|
<p className="help">
|
|
{__('This will prevent you from purchasing any content over a certain cost, as a safety measure.')}
|
|
</p>
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
{/* @endif */}
|
|
|
|
<Card
|
|
title={__('Purchase and tip confirmations')}
|
|
actions={
|
|
<React.Fragment>
|
|
<FormField
|
|
type="radio"
|
|
name="confirm_all_purchases"
|
|
checked={!instantPurchaseEnabled}
|
|
label={__('Always confirm before purchasing content or tipping')}
|
|
onChange={() => {
|
|
this.onInstantPurchaseEnabledChange(false);
|
|
}}
|
|
/>
|
|
<FormField
|
|
type="radio"
|
|
name="instant_purchases"
|
|
checked={instantPurchaseEnabled}
|
|
label={__('Only confirm purchases or tips over a certain amount')}
|
|
onChange={() => {
|
|
this.onInstantPurchaseEnabledChange(true);
|
|
}}
|
|
/>
|
|
|
|
{instantPurchaseEnabled && (
|
|
<FormFieldPrice
|
|
name="confirmation_price"
|
|
min={0.1}
|
|
onChange={this.onInstantPurchaseMaxChange}
|
|
price={instantPurchaseMax}
|
|
/>
|
|
)}
|
|
|
|
<p className="help">
|
|
{__(
|
|
"When this option is chosen, LBRY won't ask you to confirm downloads or tips below your chosen amount."
|
|
)}
|
|
</p>
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
|
|
{(isAuthenticated || !IS_WEB) && (
|
|
<Card
|
|
title={__('Wallet security')}
|
|
actions={
|
|
<React.Fragment>
|
|
{/* @if TARGET='app' */}
|
|
<FormField
|
|
disabled
|
|
type="checkbox"
|
|
name="encrypt_wallet"
|
|
onChange={() => this.onChangeEncryptWallet()}
|
|
checked={walletEncrypted}
|
|
label={__('Encrypt my wallet with a custom password')}
|
|
helper={
|
|
<React.Fragment>
|
|
<I18nMessage
|
|
tokens={{
|
|
learn_more: (
|
|
<Button
|
|
button="link"
|
|
label={__('Learn more')}
|
|
href="https://lbry.com/faq/account-sync"
|
|
/>
|
|
),
|
|
}}
|
|
>
|
|
Wallet encryption is currently unavailable until it's supported for synced accounts. It will
|
|
be added back soon. %learn_more%.
|
|
</I18nMessage>
|
|
{/* {__('Secure your local wallet data with a custom password.')}{' '}
|
|
<strong>{__('Lost passwords cannot be recovered.')} </strong>
|
|
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/wallet-encryption" />. */}
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
|
|
{walletEncrypted && storedPassword && (
|
|
<FormField
|
|
type="checkbox"
|
|
name="save_password"
|
|
onChange={this.onConfirmForgetPassword}
|
|
checked={storedPassword}
|
|
label={__('Save Password')}
|
|
helper={<React.Fragment>{__('Automatically unlock your wallet on startup')}</React.Fragment>}
|
|
/>
|
|
)}
|
|
{/* @endif */}
|
|
|
|
<FormField
|
|
type="checkbox"
|
|
name="hide_balance"
|
|
onChange={() => setClientSetting(SETTINGS.HIDE_BALANCE, !hideBalance)}
|
|
checked={hideBalance}
|
|
label={__('Hide wallet balance in header')}
|
|
/>
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{/* @if TARGET='app' */}
|
|
<Card
|
|
title={
|
|
<span>
|
|
{__('Automatic transcoding')}
|
|
{findingFFmpeg && <Spinner type="small" />}
|
|
</span>
|
|
}
|
|
actions={
|
|
<React.Fragment>
|
|
<FileSelector
|
|
type="openDirectory"
|
|
placeholder={__('A Folder containing FFmpeg')}
|
|
currentPath={ffmpegPath || daemonSettings.ffmpeg_path}
|
|
onFileChosen={(newDirectory: WebFile) => {
|
|
// $FlowFixMe
|
|
this.onFFmpegFolder(newDirectory.path);
|
|
}}
|
|
disabled={Boolean(ffmpegPath)}
|
|
/>
|
|
<p className="help">
|
|
{ffmpegAvailable ? (
|
|
<I18nMessage
|
|
tokens={{
|
|
learn_more: (
|
|
<Button
|
|
button="link"
|
|
label={__('Learn more')}
|
|
href="https://lbry.com/faq/video-publishing-guide#automatic"
|
|
/>
|
|
),
|
|
}}
|
|
>
|
|
FFmpeg is correctly configured. %learn_more%
|
|
</I18nMessage>
|
|
) : (
|
|
<I18nMessage
|
|
tokens={{
|
|
check_again: (
|
|
<Button
|
|
button="link"
|
|
label={__('Check again')}
|
|
onClick={() => this.findFFmpeg()}
|
|
disabled={findingFFmpeg}
|
|
/>
|
|
),
|
|
learn_more: (
|
|
<Button
|
|
button="link"
|
|
label={__('Learn more')}
|
|
href="https://lbry.com/faq/video-publishing-guide#automatic"
|
|
/>
|
|
),
|
|
}}
|
|
>
|
|
FFmpeg could not be found. Navigate to it or Install, Then %check_again% or quit and restart the
|
|
app. %learn_more%
|
|
</I18nMessage>
|
|
)}
|
|
</p>
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
{/* @endif */}
|
|
{!IS_WEB && (
|
|
<Card
|
|
title={__('Experimental settings')}
|
|
actions={
|
|
<React.Fragment>
|
|
{/* @if TARGET='app' */}
|
|
{/*
|
|
Disabling below until we get downloads to work with shared subscriptions code
|
|
<FormField
|
|
type="checkbox"
|
|
name="auto_download"
|
|
onChange={() => setClientSetting(SETTINGS.AUTO_DOWNLOAD, !autoDownload)}
|
|
checked={autoDownload}
|
|
label={__('Automatically download new content from my subscriptions')}
|
|
helper={__(
|
|
"The latest file from each of your subscriptions will be downloaded for quick access as soon as it's published."
|
|
)}
|
|
/> */}
|
|
<fieldset-section>
|
|
<FormField
|
|
name="max_connections"
|
|
type="select"
|
|
label={__('Max Connections')}
|
|
helper={__(
|
|
'For users with good bandwidth, try a higher value to improve streaming and download speeds. Low bandwidth users may benefit from a lower setting. Default is 4.'
|
|
)}
|
|
min={1}
|
|
max={100}
|
|
onChange={this.onMaxConnectionsChange}
|
|
value={daemonSettings.max_connections_per_download}
|
|
>
|
|
{connectionOptions.map(connectionOption => (
|
|
<option key={connectionOption} value={connectionOption}>
|
|
{connectionOption}
|
|
</option>
|
|
))}
|
|
</FormField>
|
|
</fieldset-section>
|
|
<SettingWalletServer />
|
|
{/* @endif */}
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{/* @if TARGET='app' */}
|
|
{/* Auto launch in a hidden state doesn't work on mac https://github.com/Teamwork/node-auto-launch/issues/81 */}
|
|
{!IS_MAC && <Card title={__('Startup preferences')} actions={<SettingAutoLaunch />} />}
|
|
<Card title={__('Closing preferences')} actions={<SettingClosingBehavior />} />
|
|
{/* @endif */}
|
|
</div>
|
|
)}
|
|
</Page>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default SettingsPage;
|