lbry-desktop/ui/page/settingsAdvanced/view.jsx
infinite-persistence ff9ca662f2
Option to change commments-server (desktop)
## Issue
> 5459 Add setting for changing your comment server. Visible on desktop (and possibly defaulting to Odysee URL), hidden on odysee.

## Comments
Not sure how this would actually work properly without the user recompiling the app to handle server differences. For example, even when we use our own server but switch between v1 and v2, some code changes are need to handle the differences. At that point, it seems easier for the user to just change the .env file? Anyway...

## Changes
- Added Desktop-only options to define custom server. [Settings > Advanced Settings > "Comment server" section].
2021-08-12 14:01:22 +08:00

529 lines
19 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 SettingCommentsServer from 'component/settingCommentsServer';
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';
import PublishSettings from 'component/publishSettings';
// @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 SettingsAdvancedPage 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={__('Network 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 purchases 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' */}
<Card title={__('Comments server')} actions={<SettingCommentsServer />} />
{/* @endif */}
<Card title={__('Upload settings')} actions={<PublishSettings />} />
{/* @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 SettingsAdvancedPage;