From 3b080012acf44eca23043009f50c8f00fc6d551a Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 5 Aug 2021 16:42:37 +0800
Subject: [PATCH 01/36] Styles for new Settings Page
---
ui/component/settingsRow/index.js | 2 +
ui/component/settingsRow/view.jsx | 36 +++++++++++++
ui/scss/component/section.scss | 90 ++++++++++++++++++++++++++++++-
3 files changed, 127 insertions(+), 1 deletion(-)
create mode 100644 ui/component/settingsRow/index.js
create mode 100644 ui/component/settingsRow/view.jsx
diff --git a/ui/component/settingsRow/index.js b/ui/component/settingsRow/index.js
new file mode 100644
index 000000000..a8d6ab489
--- /dev/null
+++ b/ui/component/settingsRow/index.js
@@ -0,0 +1,2 @@
+import SettingsRow from './view';
+export default SettingsRow;
diff --git a/ui/component/settingsRow/view.jsx b/ui/component/settingsRow/view.jsx
new file mode 100644
index 000000000..a59b7e52b
--- /dev/null
+++ b/ui/component/settingsRow/view.jsx
@@ -0,0 +1,36 @@
+// @flow
+import React from 'react';
+import classnames from 'classnames';
+
+type Props = {
+ title: string,
+ subtitle?: string,
+ multirow?: boolean, // Displays the Value widget(s) below the Label instead of on the right.
+ useVerticalSeparator?: boolean, // Show a separator line between Label and Value. Useful when there are multiple Values.
+ children?: React$Node,
+};
+
+export default function SettingsRow(props: Props) {
+ const { title, subtitle, multirow, useVerticalSeparator, children } = props;
+
+ return (
+
+
+
{title}
+ {subtitle &&
{subtitle}
}
+
+
+ {children && children}
+
+
+ );
+}
diff --git a/ui/scss/component/section.scss b/ui/scss/component/section.scss
index 2c6fd46f8..a45b88a7e 100644
--- a/ui/scss/component/section.scss
+++ b/ui/scss/component/section.scss
@@ -140,7 +140,7 @@
margin-right: var(--spacing-s);
}
- @media (max-width: $breakpoint-small) {
+ @media (max-width: $breakpoint-medium) {
flex-wrap: wrap;
> * {
@@ -203,3 +203,91 @@
margin-right: 10px;
}
}
+
+.settings__row {
+ &:first-child,
+ &:only-child {
+ border-top: none;
+ }
+}
+
+.settings__row--title {
+ min-width: 100%;
+ align-self: flex-start;
+
+ @media (min-width: $breakpoint-small) {
+ min-width: 60%;
+ max-width: 60%;
+ }
+}
+
+.settings__row--subtitle {
+ @extend .section__subtitle;
+ font-size: var(--font-small);
+ margin-top: calc(var(--spacing-xxs) / 2);
+}
+
+.settings__row--value {
+ width: 100%;
+
+ fieldset-section:not(:only-child) {
+ margin-top: var(--spacing-s);
+ }
+
+ fieldset-section.radio {
+ margin-top: var(--spacing-s);
+ }
+
+ fieldset-group {
+ margin-top: var(--spacing-m);
+ }
+
+ .tags--remove {
+ margin-bottom: 0;
+ }
+
+ .tags__input-wrapper {
+ .tag__input {
+ height: unset;
+ max-width: unset;
+ }
+ }
+
+ .form-field--price-amount {
+ max-width: unset;
+ }
+
+ @media (min-width: $breakpoint-medium) {
+ width: 40%;
+ margin-left: var(--spacing-m);
+ padding-left: var(--spacing-m);
+
+ .button,
+ .checkbox {
+ &:only-child {
+ float: right;
+ }
+ }
+
+ input {
+ align-self: flex-end;
+ }
+ }
+}
+
+.settings__row--value--multirow {
+ @media (min-width: $breakpoint-medium) {
+ width: 80%;
+ margin-top: var(--spacing-l);
+
+ input {
+ align-self: flex-start;
+ }
+ }
+}
+
+.settings__row--value--vertical-separator {
+ @media (min-width: $breakpoint-medium) {
+ border-left: 1px solid var(--color-border);
+ }
+}
From 04b510d88b9ba923b34d864da8412404e70a8a80 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 5 Aug 2021 15:00:21 +0800
Subject: [PATCH 02/36] [Account] grab SyncToggle and AccountPassword
Also made visual changes for the new Settings Page.
## SyncToggle:
It will no longer be under a dedicated Card, so the "Sync" title is not there to give context for the case of "no verified email".
Changed it such that the checkbox is always visible (it's label is self-explanatory) but disable when email is not set. The "Add Email" button will then appear below, so everything now makes sense in context.
---
ui/component/settingAccount/index.js | 15 ++++
ui/component/settingAccount/view.jsx | 55 ++++++++++++
ui/component/settingAccountPassword/view.jsx | 95 ++++++++++----------
ui/component/syncToggle/view.jsx | 30 ++++---
ui/page/settings/index.js | 7 +-
ui/page/settings/view.jsx | 50 ++---------
6 files changed, 144 insertions(+), 108 deletions(-)
create mode 100644 ui/component/settingAccount/index.js
create mode 100644 ui/component/settingAccount/view.jsx
diff --git a/ui/component/settingAccount/index.js b/ui/component/settingAccount/index.js
new file mode 100644
index 000000000..9abbe808b
--- /dev/null
+++ b/ui/component/settingAccount/index.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import { doWalletStatus, selectWalletIsEncrypted } from 'lbry-redux';
+import { selectUserVerifiedEmail } from 'redux/selectors/user';
+import SettingAccount from './view';
+
+const select = (state) => ({
+ isAuthenticated: selectUserVerifiedEmail(state),
+ walletEncrypted: selectWalletIsEncrypted(state),
+});
+
+const perform = (dispatch) => ({
+ doWalletStatus: () => dispatch(doWalletStatus()),
+});
+
+export default connect(select, perform)(SettingAccount);
diff --git a/ui/component/settingAccount/view.jsx b/ui/component/settingAccount/view.jsx
new file mode 100644
index 000000000..4097b532d
--- /dev/null
+++ b/ui/component/settingAccount/view.jsx
@@ -0,0 +1,55 @@
+// @flow
+import React from 'react';
+import Card from 'component/common/card';
+import SettingAccountPassword from 'component/settingAccountPassword';
+import SyncToggle from 'component/syncToggle';
+import { getPasswordFromCookie } from 'util/saved-passwords';
+
+type Props = {
+ // --- select ---
+ isAuthenticated: boolean,
+ walletEncrypted: boolean,
+ // --- perform ---
+ doWalletStatus: () => void,
+};
+
+export default function SettingAccount(props: Props) {
+ const { isAuthenticated, walletEncrypted, doWalletStatus } = props;
+ const [storedPassword, setStoredPassword] = React.useState(false);
+
+ // Determine if password is stored.
+ React.useEffect(() => {
+ if (isAuthenticated || !IS_WEB) {
+ doWalletStatus();
+ getPasswordFromCookie().then((p) => {
+ if (typeof p === 'string') {
+ setStoredPassword(true);
+ }
+ });
+ }
+ // enterSettings(); @KP need to do this at each component, or just at Settings Page?
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+ return (
+
+ {isAuthenticated && (
+
+
+
+ )}
+
+ {/* @if TARGET='app' */}
+
+
+
+ {/* @endif */}
+ >
+ }
+ />
+ );
+}
diff --git a/ui/component/settingAccountPassword/view.jsx b/ui/component/settingAccountPassword/view.jsx
index a63dff59a..494d078e6 100644
--- a/ui/component/settingAccountPassword/view.jsx
+++ b/ui/component/settingAccountPassword/view.jsx
@@ -3,7 +3,6 @@ import React, { useState } from 'react';
import { FormField, Form } from 'component/common/form';
import Button from 'component/button';
import ErrorText from 'component/common/error-text';
-import Card from 'component/common/card';
import * as PAGES from 'constants/pages';
type Props = {
@@ -38,54 +37,52 @@ export default function SettingAccountPassword(props: Props) {
}
}, [passwordSetSuccess, setOldPassword, setNewPassword, doClearPasswordEntry, doToast]);
- return (
-
-
- {passwordSetError && (
-
- {passwordSetError}
-
- )}
-
- ) : (
-
+
+
+ {isAuthenticated && (
+
+
+
+ )}
+
+ {/* @if TARGET='app' */}
-
+
- )}
+ {/* @endif */}
- {/* @if TARGET='app' */}
-
-
-
- {/* @endif */}
+ {/* @if TARGET='web' */}
+ {user && getStripeEnvironment() && (
+
+
+
+ )}
+ {/* @endif */}
- {/* @if TARGET='web' */}
- {user && getStripeEnvironment() && (
-
-
-
- )}
- {/* @endif */}
-
- {/* @if TARGET='web' */}
- {isAuthenticated && getStripeEnvironment() && (
-
-
-
- )}
- {/* @endif */}
- >
- }
- />
+ {/* @if TARGET='web' */}
+ {isAuthenticated && getStripeEnvironment() && (
+
+
+
+ )}
+ {/* @endif */}
+ >
+ }
+ />
+ >
);
}
diff --git a/ui/component/settingAppearance/view.jsx b/ui/component/settingAppearance/view.jsx
index be62e926d..436e390b5 100644
--- a/ui/component/settingAppearance/view.jsx
+++ b/ui/component/settingAppearance/view.jsx
@@ -24,62 +24,65 @@ export default function SettingAppearance(props: Props) {
const { clock24h, searchInLanguage, isAuthenticated, hideBalance, setClientSetting, setSearchInLanguage } = props;
return (
-
- {homepages && Object.keys(homepages).length > 1 && (
-
-
+ <>
+
+
{__('Appearance')}
+
+
+ {homepages && Object.keys(homepages).length > 1 && (
+
+
+
+ )}
+
+
+
- )}
-
-
-
-
-
- setSearchInLanguage(!searchInLanguage)}
- />
-
-
-
-
-
-
-
- setClientSetting(SETTINGS.CLOCK_24H, !clock24h)}
- checked={clock24h}
- />
-
-
- {(isAuthenticated || !IS_WEB) && (
-
+
setClientSetting(SETTINGS.HIDE_BALANCE, !hideBalance)}
- checked={hideBalance}
+ checked={searchInLanguage}
+ onChange={() => setSearchInLanguage(!searchInLanguage)}
/>
- )}
- >
- }
- />
+
+
+
+
+
+
+ setClientSetting(SETTINGS.CLOCK_24H, !clock24h)}
+ checked={clock24h}
+ />
+
+
+ {(isAuthenticated || !IS_WEB) && (
+
+ setClientSetting(SETTINGS.HIDE_BALANCE, !hideBalance)}
+ checked={hideBalance}
+ />
+
+ )}
+ >
+ }
+ />
+ >
);
}
-// prettier-disable
+// prettier-ignore
const HELP = {
LANGUAGE: 'Multi-language support is brand new and incomplete. Switching your language may have unintended consequences, like glossolalia.',
};
diff --git a/ui/component/settingContent/view.jsx b/ui/component/settingContent/view.jsx
index fb1dd5e74..42c2dbd17 100644
--- a/ui/component/settingContent/view.jsx
+++ b/ui/component/settingContent/view.jsx
@@ -52,51 +52,53 @@ export default function SettingContent(props: Props) {
} = props;
return (
-
-
- {
- setClientSetting(SETTINGS.FLOATING_PLAYER, !floatingPlayer);
- clearPlayingUri();
- }}
- checked={floatingPlayer}
- />
-
+ <>
+
+
{__('Content settings')}
+
+
+
+ {
+ setClientSetting(SETTINGS.FLOATING_PLAYER, !floatingPlayer);
+ clearPlayingUri();
+ }}
+ checked={floatingPlayer}
+ />
+
-
- setClientSetting(SETTINGS.AUTOPLAY, !autoplay)}
- checked={autoplay}
- />
-
+
+ setClientSetting(SETTINGS.AUTOPLAY, !autoplay)}
+ checked={autoplay}
+ />
+
- {!SIMPLE_SITE && (
- <>
-
- {
- if (isAuthenticated) {
- let param = e.target.checked ? { add: 'noreposts' } : { remove: 'noreposts' };
- Lbryio.call('user_tag', 'edit', param);
- }
- setClientSetting(SETTINGS.HIDE_REPOSTS, !hideReposts);
- }}
- />
-
+ {!SIMPLE_SITE && (
+ <>
+
+ {
+ if (isAuthenticated) {
+ let param = e.target.checked ? { add: 'noreposts' } : { remove: 'noreposts' };
+ Lbryio.call('user_tag', 'edit', param);
+ }
+ setClientSetting(SETTINGS.HIDE_REPOSTS, !hideReposts);
+ }}
+ />
+
- {/*
+ {/*
*/}
-
-
- !IS_WEB || showNsfw
- ? setClientSetting(SETTINGS.SHOW_MATURE, !showNsfw)
- : openModal(MODALS.CONFIRM_AGE)
- }
- />
-
- >
- )}
+
+
+ !IS_WEB || showNsfw
+ ? setClientSetting(SETTINGS.SHOW_MATURE, !showNsfw)
+ : openModal(MODALS.CONFIRM_AGE)
+ }
+ />
+
+ >
+ )}
- {(isAuthenticated || !IS_WEB) && (
- <>
-
-
-
-
-
-
-
-
- {myChannelUrls && myChannelUrls.length > 0 && (
-
+ {(isAuthenticated || !IS_WEB) && (
+ <>
+
- )}
- >
- )}
-
- setClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW, !enablePublishPreview)}
- />
-
+
+
+
- {/* @if TARGET='app' */}
-
-
-
- {/* @endif */}
-
-
- setClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, false)}
- />
- setClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, true)}
- />
- {instantPurchaseEnabled && (
- setClientSetting(SETTINGS.INSTANT_PURCHASE_MAX, newValue)}
- price={instantPurchaseMax}
- />
+ {myChannelUrls && myChannelUrls.length > 0 && (
+
+
+
+ )}
+ >
)}
-
- >
- }
- />
+
+
+ setClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW, !enablePublishPreview)}
+ />
+
+
+ {/* @if TARGET='app' */}
+
+
+
+ {/* @endif */}
+
+
+ setClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, false)}
+ />
+ setClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, true)}
+ />
+ {instantPurchaseEnabled && (
+ setClientSetting(SETTINGS.INSTANT_PURCHASE_MAX, newValue)}
+ price={instantPurchaseMax}
+ />
+ )}
+
+ >
+ }
+ />
+ >
);
}
diff --git a/ui/component/settingSystem/view.jsx b/ui/component/settingSystem/view.jsx
index 21894c3be..7f4bbd138 100644
--- a/ui/component/settingSystem/view.jsx
+++ b/ui/component/settingSystem/view.jsx
@@ -121,222 +121,224 @@ export default function SettingSystem(props: Props) {
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return (
-
- {/* @if TARGET='app' */}
-
- {
- setDaemonSetting('download_dir', newDirectory.path);
- }}
- />
-
- {/* @endif */}
+ <>
+
+
{__('System')}
+
+
+ {/* @if TARGET='app' */}
+
+ {
+ setDaemonSetting('download_dir', newDirectory.path);
+ }}
+ />
+
+ {/* @endif */}
- {/* @if TARGET='app' */}
-
- setDaemonSetting('save_files', !daemonSettings.save_files)}
- checked={daemonSettings.save_files}
- />
-
-
- {__("If disabled, LBRY will be very sad and you won't be helping improve the network.")}{' '}
- .
-
- }
- >
- setDaemonSetting('save_blobs', !daemonSettings.save_blobs)}
- checked={daemonSettings.save_blobs}
- />
-
- {/* @endif */}
-
- {/* @if TARGET='app' */}
-
- {__(
- `This is information like error logging, performance tracking, and usage statistics. It includes your IP address and basic system details, but no other identifying information (unless you sign in to lbry.tv)`
- )}{' '}
-
-
- }
- useVerticalSeparator
- >
- setDaemonSetting('share_usage_data', !daemonSettings.share_usage_data)}
- checked={daemonSettings.share_usage_data}
- label={{__('Allow the app to share data to LBRY.inc')}}
- helper={
- isAuthenticated
- ? __('Internal sharing is required while signed in.')
- : __('Internal sharing is required to participate in rewards programs.')
- }
- disabled={isAuthenticated && daemonSettings.share_usage_data}
- />
- toggle3PAnalytics(e.target.checked)}
- checked={allowAnalytics}
- label={__('Allow the app to access third party analytics platforms')}
- helper={__('We use detailed analytics to improve all aspects of the LBRY experience.')}
- />
-
- {/* @endif */}
-
- {/* @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 && (
+ {/* @if TARGET='app' */}
-
+ setDaemonSetting('save_files', !daemonSettings.save_files)}
+ checked={daemonSettings.save_files}
+ />
- )}
- {/* @endif */}
-
- {/* @if TARGET='app' */}
-
-
-
- {/* @endif */}
-
- {/* @if TARGET='app' */}
-
- {__('Automatic transcoding')}
- {findingFFmpeg && }
-
- }
- >
- {
- // $FlowFixMe
- setDaemonSetting('ffmpeg_path', newDirectory.path);
- findFFmpeg();
- }}
- disabled={Boolean(ffmpegPath)}
- />
-
- {ffmpegAvailable ? (
-
- ),
- }}
- >
- FFmpeg is correctly configured. %learn_more%
-
- ) : (
- findFFmpeg()}
- disabled={findingFFmpeg}
- />
- ),
- learn_more: (
-
- ),
- }}
- >
- FFmpeg could not be found. Navigate to it or Install, Then %check_again% or quit and restart the app.
- %learn_more%
-
- )}
-
-
- {/* @endif */}
-
- {/* @if TARGET='app' */}
-
- onChangeEncryptWallet()}
- checked={walletEncrypted}
- label={__('Encrypt my wallet with a custom password')}
- helper={
+
+ {__("If disabled, LBRY will be very sad and you won't be helping improve the network.")}{' '}
+ .
+
+ }
+ >
+ setDaemonSetting('save_blobs', !daemonSettings.save_blobs)}
+ checked={daemonSettings.save_blobs}
+ />
+
+ {/* @endif */}
+
+ {/* @if TARGET='app' */}
+
+ {__(
+ `This is information like error logging, performance tracking, and usage statistics. It includes your IP address and basic system details, but no other identifying information (unless you sign in to lbry.tv)`
+ )}{' '}
+
+
+ }
+ useVerticalSeparator
+ >
+ setDaemonSetting('share_usage_data', !daemonSettings.share_usage_data)}
+ checked={daemonSettings.share_usage_data}
+ label={{__('Allow the app to share data to LBRY.inc')}}
+ helper={
+ isAuthenticated
+ ? __('Internal sharing is required while signed in.')
+ : __('Internal sharing is required to participate in rewards programs.')
+ }
+ disabled={isAuthenticated && daemonSettings.share_usage_data}
+ />
+ toggle3PAnalytics(e.target.checked)}
+ checked={allowAnalytics}
+ label={__('Allow the app to access third party analytics platforms')}
+ helper={__('We use detailed analytics to improve all aspects of the LBRY experience.')}
+ />
+
+ {/* @endif */}
+
+ {/* @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 && (
+
+
+
+ )}
+ {/* @endif */}
+
+ {/* @if TARGET='app' */}
+
+
+
+ {/* @endif */}
+
+ {/* @if TARGET='app' */}
+
+ {__('Automatic transcoding')}
+ {findingFFmpeg && }
+
+ }
+ >
+ {
+ // $FlowFixMe
+ setDaemonSetting('ffmpeg_path', newDirectory.path);
+ findFFmpeg();
+ }}
+ disabled={Boolean(ffmpegPath)}
+ />
+
+ {ffmpegAvailable ? (
+
),
}}
>
- Wallet encryption is currently unavailable until it's supported for synced accounts. It will be
- added back soon. %learn_more%.
+ FFmpeg is correctly configured. %learn_more%
- {/* {__('Secure your local wallet data with a custom password.')}{' '}
+ ) : (
+ findFFmpeg()}
+ disabled={findingFFmpeg}
+ />
+ ),
+ learn_more: (
+
+ ),
+ }}
+ >
+ FFmpeg could not be found. Navigate to it or Install, Then %check_again% or quit and restart the
+ app. %learn_more%
+
+ )}
+
+
+ {/* @endif */}
+
+ {/* @if TARGET='app' */}
+
+ onChangeEncryptWallet()}
+ checked={walletEncrypted}
+ label={__('Encrypt my wallet with a custom password')}
+ helper={
+
+
+ ),
+ }}
+ >
+ Wallet encryption is currently unavailable until it's supported for synced accounts. It will be
+ added back soon. %learn_more%.
+
+ {/* {__('Secure your local wallet data with a custom password.')}{' '}
{__('Lost passwords cannot be recovered.')}
. */}
-
- }
- />
-
- {walletEncrypted && storedPassword && (
- {__('Automatically unlock your wallet on startup')}}
+
+ }
/>
- )}
-
- {/* @endif */}
- {/* @if TARGET='app' */}
-
- {/* Disabling below until we get downloads to work with shared subscriptions code */}
- {/*
+ {walletEncrypted && storedPassword && (
+ {__('Automatically unlock your wallet on startup')}}
+ />
+ )}
+
+ {/* @endif */}
+
+ {/* @if TARGET='app' */}
+
+ {/* Disabling below until we get downloads to work with shared subscriptions code */}
+ {/*
*/}
-
- setDaemonSetting('max_connections_per_download', e.target.value)}
- value={daemonSettings.max_connections_per_download}
- >
- {[1, 2, 4, 6, 10, 20].map((connectionOption) => (
-
- ))}
-
-
+
+ setDaemonSetting('max_connections_per_download', e.target.value)}
+ value={daemonSettings.max_connections_per_download}
+ >
+ {[1, 2, 4, 6, 10, 20].map((connectionOption) => (
+
+ ))}
+
+
-
-
-
- {/* @endif */}
+
+
+
+ {/* @endif */}
-
- {
- setClearingCache(true);
- clearCache();
- }}
- disabled={clearingCache}
- />
-
- >
- }
- />
+
+ {
+ setClearingCache(true);
+ clearCache();
+ }}
+ disabled={clearingCache}
+ />
+
+ >
+ }
+ />
+ >
);
}
From 6e152a413720f0d4caa68c13dab7bcb395d8b8d5 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Mon, 9 Aug 2021 13:50:11 +0800
Subject: [PATCH 26/36] Re-render on language change
Room for improvement: refactor into `` so we don't have to repeat this everywhere.
---
ui/component/settingAccount/index.js | 3 +++
ui/component/settingAppearance/index.js | 3 ++-
ui/component/settingContent/index.js | 4 +++-
ui/component/settingSystem/index.js | 9 ++++++++-
ui/component/settingUnauthenticated/index.js | 3 ++-
ui/page/settings/index.js | 6 ++++--
6 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/ui/component/settingAccount/index.js b/ui/component/settingAccount/index.js
index 596776c1f..83d77af76 100644
--- a/ui/component/settingAccount/index.js
+++ b/ui/component/settingAccount/index.js
@@ -1,12 +1,15 @@
import { connect } from 'react-redux';
import { doWalletStatus, selectWalletIsEncrypted } from 'lbry-redux';
import { selectUser, selectUserVerifiedEmail } from 'redux/selectors/user';
+import { selectLanguage } from 'redux/selectors/settings';
+
import SettingAccount from './view';
const select = (state) => ({
isAuthenticated: selectUserVerifiedEmail(state),
walletEncrypted: selectWalletIsEncrypted(state),
user: selectUser(state),
+ language: selectLanguage(state),
});
const perform = (dispatch) => ({
diff --git a/ui/component/settingAppearance/index.js b/ui/component/settingAppearance/index.js
index d00c404e8..33cea31fc 100644
--- a/ui/component/settingAppearance/index.js
+++ b/ui/component/settingAppearance/index.js
@@ -1,7 +1,7 @@
import { connect } from 'react-redux';
import { SETTINGS } from 'lbry-redux';
import { doSetClientSetting } from 'redux/actions/settings';
-import { makeSelectClientSetting } from 'redux/selectors/settings';
+import { selectLanguage, makeSelectClientSetting } from 'redux/selectors/settings';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import SettingAppearance from './view';
@@ -10,6 +10,7 @@ const select = (state) => ({
searchInLanguage: makeSelectClientSetting(SETTINGS.SEARCH_IN_LANGUAGE)(state),
isAuthenticated: selectUserVerifiedEmail(state),
hideBalance: makeSelectClientSetting(SETTINGS.HIDE_BALANCE)(state),
+ language: selectLanguage(state),
});
const perform = (dispatch) => ({
diff --git a/ui/component/settingContent/index.js b/ui/component/settingContent/index.js
index 766f5efff..706b8562b 100644
--- a/ui/component/settingContent/index.js
+++ b/ui/component/settingContent/index.js
@@ -3,8 +3,9 @@ import { selectMyChannelUrls, SETTINGS } from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app';
import { doSetPlayingUri } from 'redux/actions/content';
import { doSetClientSetting } from 'redux/actions/settings';
-import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
+import { selectShowMatureContent, selectLanguage, makeSelectClientSetting } from 'redux/selectors/settings';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
+
import SettingContent from './view';
const select = (state) => ({
@@ -17,6 +18,7 @@ const select = (state) => ({
instantPurchaseEnabled: makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED)(state),
instantPurchaseMax: makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state),
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
+ language: selectLanguage(state),
});
const perform = (dispatch) => ({
diff --git a/ui/component/settingSystem/index.js b/ui/component/settingSystem/index.js
index 1d7cacc02..53155e49d 100644
--- a/ui/component/settingSystem/index.js
+++ b/ui/component/settingSystem/index.js
@@ -9,8 +9,14 @@ import {
} from 'redux/actions/app';
import { doSetDaemonSetting, doClearDaemonSetting, doFindFFmpeg } from 'redux/actions/settings';
import { selectAllowAnalytics } from 'redux/selectors/app';
-import { selectDaemonSettings, selectFfmpegStatus, selectFindingFFmpeg } from 'redux/selectors/settings';
+import {
+ selectDaemonSettings,
+ selectFfmpegStatus,
+ selectFindingFFmpeg,
+ selectLanguage,
+} from 'redux/selectors/settings';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
+
import SettingSystem from './view';
const select = (state) => ({
@@ -20,6 +26,7 @@ const select = (state) => ({
walletEncrypted: selectWalletIsEncrypted(state),
isAuthenticated: selectUserVerifiedEmail(state),
allowAnalytics: selectAllowAnalytics(state),
+ language: selectLanguage(state),
});
const perform = (dispatch) => ({
diff --git a/ui/component/settingUnauthenticated/index.js b/ui/component/settingUnauthenticated/index.js
index 7f9b623a0..8476d4a40 100644
--- a/ui/component/settingUnauthenticated/index.js
+++ b/ui/component/settingUnauthenticated/index.js
@@ -1,12 +1,13 @@
import { connect } from 'react-redux';
import { SETTINGS } from 'lbry-redux';
import { doSetClientSetting } from 'redux/actions/settings';
-import { makeSelectClientSetting } from 'redux/selectors/settings';
+import { selectLanguage, makeSelectClientSetting } from 'redux/selectors/settings';
import SettingUnauthenticated from './view';
const select = (state) => ({
searchInLanguage: makeSelectClientSetting(SETTINGS.SEARCH_IN_LANGUAGE)(state),
+ language: selectLanguage(state),
});
const perform = (dispatch) => ({
diff --git a/ui/page/settings/index.js b/ui/page/settings/index.js
index c8fa27126..35df01630 100644
--- a/ui/page/settings/index.js
+++ b/ui/page/settings/index.js
@@ -1,12 +1,14 @@
import { connect } from 'react-redux';
import { doEnterSettingsPage, doExitSettingsPage } from 'redux/actions/settings';
-import { selectDaemonSettings } from 'redux/selectors/settings';
-import SettingsPage from './view';
+import { selectDaemonSettings, selectLanguage } from 'redux/selectors/settings';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
+import SettingsPage from './view';
+
const select = (state) => ({
daemonSettings: selectDaemonSettings(state),
isAuthenticated: selectUserVerifiedEmail(state),
+ language: selectLanguage(state),
});
const perform = (dispatch) => ({
From 1671deb0b200d71c089f21cba2cc453fa0b6dd3f Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 19 Aug 2021 09:36:43 +0800
Subject: [PATCH 27/36] Page: the 'settingsPage' option now automatically adds
the sidebar.
---
ui/component/page/view.jsx | 29 +++++++++++-----
ui/page/settings/view.jsx | 70 ++++++++++++++++----------------------
2 files changed, 51 insertions(+), 48 deletions(-)
diff --git a/ui/component/page/view.jsx b/ui/component/page/view.jsx
index 76867b415..6d288c3bc 100644
--- a/ui/component/page/view.jsx
+++ b/ui/component/page/view.jsx
@@ -4,6 +4,7 @@ import React, { Fragment } from 'react';
import classnames from 'classnames';
import { lazyImport } from 'util/lazyImport';
import SideNavigation from 'component/sideNavigation';
+import SettingsSideNavigation from 'component/settingsSideNavigation';
import Header from 'component/header';
/* @if TARGET='app' */
import StatusBar from 'component/common/status-bar';
@@ -78,6 +79,24 @@ function Page(props: Props) {
const isAbsoluteSideNavHidden = (isOnFilePage || isMobile) && !sidebarOpen;
+ function getSideNavElem() {
+ if (!authPage) {
+ if (settingsPage) {
+ return ;
+ } else if (!noSideNavigation) {
+ return (
+
+ );
+ }
+ }
+ return null;
+ }
+
React.useEffect(() => {
if (isOnFilePage || isMediumScreen) {
setSidebarOpen(false);
@@ -102,14 +121,8 @@ function Page(props: Props) {
'main-wrapper__inner--theater-mode': isOnFilePage && videoTheaterMode,
})}
>
- {!authPage && !noSideNavigation && (
-
- )}
+ {getSideNavElem()}
+
{
noFooter
settingsPage
noSideNavigation
- backout={{ title: __('Settings'), backLabel: __('Done') }}
+ backout={{ title: __('Settings'), backLabel: __('Back') }}
className="card-stack"
>
-
-
-
- {!isAuthenticated && IS_WEB && (
- <>
-
-
-
-
-
- }
- />
-
- >
- )}
-
- {!IS_WEB && noDaemonSettings ? (
-
- {__('Failed to load settings.')}
-
- ) : (
-
-
-
-
-
+ {!isAuthenticated && IS_WEB && (
+ <>
+
+
+
+
+
+ }
+ />
- )}
-
+ >
+ )}
+
+ {!IS_WEB && noDaemonSettings ? (
+
+ {__('Failed to load settings.')}
+
+ ) : (
+
+
+
+
+
+
+ )}
);
}
From e1223d0d0217f12636016976caf1ac6d3b523111 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 19 Aug 2021 13:58:40 +0800
Subject: [PATCH 28/36] Convert setting sub-pages to new style
"Creator Settings", "Blocked and Muted", "Manage notifications", "Stripe"
---
static/app-strings.json | 4 +-
ui/page/listBlocked/view.jsx | 7 +-
ui/page/settingsCreator/view.jsx | 388 ++++++++++---------
ui/page/settingsNotifications/view.jsx | 144 ++++---
ui/page/settingsStripeAccount/view.jsx | 8 +-
ui/page/settingsStripeCard/view.jsx | 8 +-
web/scss/themes/odysee/dark.scss | 6 -
web/scss/themes/odysee/init/_base-theme.scss | 8 +-
8 files changed, 319 insertions(+), 254 deletions(-)
diff --git a/static/app-strings.json b/static/app-strings.json
index 3c0ea2ac6..ac86246da 100644
--- a/static/app-strings.json
+++ b/static/app-strings.json
@@ -1744,8 +1744,9 @@
"Delegation": "Delegation",
"Add moderator": "Add moderator",
"Enter a @username or URL": "Enter a @username or URL",
- "examples: @channel, @channel#3, https://odysee.com/@Odysee:8, lbry://@Odysee#8": "examples: @channel, @channel#3, https://odysee.com/@Odysee:8, lbry://@Odysee#8",
+ "Enter a channel name or URL to add as a moderator.\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8": "Enter a channel name or URL to add as a moderator.\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8",
"Moderators": "Moderators",
+ "Moderators can block channels on your behalf. Blocked channels will appear in your \"Blocked and Muted\" list.": "Moderators can block channels on your behalf. Blocked channels will appear in your \"Blocked and Muted\" list.",
"Add as moderator": "Add as moderator",
"Mute (m)": "Mute (m)",
"Playback Rate (<, >)": "Playback Rate (<, >)",
@@ -1934,6 +1935,7 @@
"Clarification": "Clarification",
"Client name": "Client name",
"Creator settings": "Creator settings",
+ "Comments and livestream chat containing these words will be blocked.": "Comments and livestream chat containing these words will be blocked.",
"Muted words": "Muted words",
"Add words": "Add words",
"Suggestions": "Suggestions",
diff --git a/ui/page/listBlocked/view.jsx b/ui/page/listBlocked/view.jsx
index 45f84d850..85f355947 100644
--- a/ui/page/listBlocked/view.jsx
+++ b/ui/page/listBlocked/view.jsx
@@ -229,7 +229,12 @@ function ListBlocked(props: Props) {
}, [stringifiedPersonalList, justPersonalBlocked, setLocalPersonalList]);
return (
-
+
{fetchingModerationBlockList && (
diff --git a/ui/page/settingsCreator/view.jsx b/ui/page/settingsCreator/view.jsx
index dd27a77e0..a067d6478 100644
--- a/ui/page/settingsCreator/view.jsx
+++ b/ui/page/settingsCreator/view.jsx
@@ -1,12 +1,15 @@
// @flow
+import * as ICONS from 'constants/icons';
import * as React from 'react';
import Card from 'component/common/card';
import TagsSearch from 'component/tagsSearch';
import Page from 'component/page';
import Button from 'component/button';
import ChannelSelector from 'component/channelSelector';
+import SettingsRow from 'component/settingsRow';
import Spinner from 'component/spinner';
import { FormField } from 'component/common/form-components/form-field';
+import Icon from 'component/common/icon';
import LbcSymbol from 'component/common/lbc-symbol';
import I18nMessage from 'component/i18nMessage';
import { isNameValid, parseURI } from 'lbry-redux';
@@ -284,194 +287,213 @@ export default function SettingsCreatorPage(props: Props) {
-
- {isBusy && (
-
-
-
- )}
- {isDisabled && (
-
- )}
- {!isBusy && !isDisabled && (
- <>
+
+
+
+ {isBusy && (
+
+
+
+ )}
+
+ {isDisabled && (
- setSettings({ comments_enabled: !commentsEnabled })}
- />
- {
- const value = parseInt(e.target.value);
- setSlowModeMin(value);
- pushSlowModeMinDebounced(value, activeChannelClaim);
- }}
- onBlur={() => setLastUpdated(Date.now())}
- />
- >
- }
+ title={__('Settings unavailable for this channel')}
+ subtitle={__("This channel isn't staking enough LBRY Credits to enable Creator Settings.")}
/>
-
-
-
- }
- />
-
- }}>Minimum %lbc% tip amount for comments
- }
- helper={__(
- 'Enabling a minimum amount to comment will force all comments, including livestreams, to have tips associated with them. This can help prevent spam.'
- )}
- className="form-field--price-amount"
- max={LBC_MAX}
- min={LBC_MIN}
- step={LBC_STEP}
- type="number"
- placeholder="1"
- value={minTip}
- onChange={(e) => {
- const newMinTip = parseFloat(e.target.value);
- setMinTip(newMinTip);
- pushMinTipDebounced(newMinTip, activeChannelClaim);
- if (newMinTip !== 0 && minSuper !== 0) {
- setMinSuper(0);
- pushMinSuperDebounced(0, activeChannelClaim);
- }
- }}
- onBlur={() => setLastUpdated(Date.now())}
- />
- }}>Minimum %lbc% tip amount for hyperchats
- }
- helper={
- <>
- {__(
- 'Enabling a minimum amount to hyperchat will force all TIPPED comments to have this value in order to be shown. This still allows regular comments to be posted.'
- )}
- {minTip !== 0 && (
-
- {__('(This settings is not applicable if all comments require a tip.)')}
-
- )}
- >
- }
- className="form-field--price-amount"
- min={0}
- step="any"
- type="number"
- placeholder="1"
- value={minSuper}
- disabled={minTip !== 0}
- onChange={(e) => {
- const newMinSuper = parseFloat(e.target.value);
- setMinSuper(newMinSuper);
- pushMinSuperDebounced(newMinSuper, activeChannelClaim);
- }}
- onBlur={() => setLastUpdated(Date.now())}
- />
- >
- }
- />
-
- setModeratorSearchTerm(e.target.value)}
- error={moderatorSearchError}
- />
- {moderatorSearchClaimUri && (
-
- {
- return (
- handleChannelSearchSelect(claim)}
- />
- );
- }}
+ )}
+
+ {!isBusy && !isDisabled && (
+ <>
+
+
+ setSettings({ comments_enabled: !commentsEnabled })}
/>
-
- )}
-
-
- }
- />
- >
- )}
+
+
+
+ {
+ const value = parseInt(e.target.value);
+ setSlowModeMin(value);
+ pushSlowModeMinDebounced(value, activeChannelClaim);
+ }}
+ onBlur={() => setLastUpdated(Date.now())}
+ />
+
+
+ }}>Minimum %lbc% tip amount for comments
+ }
+ subtitle={__(HELP.MIN_TIP)}
+ >
+ {
+ const newMinTip = parseFloat(e.target.value);
+ setMinTip(newMinTip);
+ pushMinTipDebounced(newMinTip, activeChannelClaim);
+ if (newMinTip !== 0 && minSuper !== 0) {
+ setMinSuper(0);
+ pushMinSuperDebounced(0, activeChannelClaim);
+ }
+ }}
+ onBlur={() => setLastUpdated(Date.now())}
+ />
+
+
+ }}>Minimum %lbc% tip amount for hyperchats
+ }
+ subtitle={
+ <>
+ {__(HELP.MIN_SUPER)}
+ {minTip !== 0 && (
+
+ {__(HELP.MIN_SUPER_OFF)}
+
+ )}
+ >
+ }
+ >
+ {
+ const newMinSuper = parseFloat(e.target.value);
+ setMinSuper(newMinSuper);
+ pushMinSuperDebounced(newMinSuper, activeChannelClaim);
+ }}
+ onBlur={() => setLastUpdated(Date.now())}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ {__('Search channel')}
+
+ >
+ }
+ placeholder={__('Enter a @username or URL')}
+ value={moderatorSearchTerm}
+ onChange={(e) => setModeratorSearchTerm(e.target.value)}
+ error={moderatorSearchError}
+ />
+ {moderatorSearchClaimUri && (
+
+ {
+ return (
+ handleChannelSearchSelect(claim)}
+ />
+ );
+ }}
+ />
+
+ )}
+
+
+ >
+ }
+ />
+ >
+ )}
+
);
}
+
+// prettier-ignore
+const HELP = {
+ SLOW_MODE: 'Minimum time gap in seconds between comments (affects livestream chat as well).',
+ MIN_TIP: 'Enabling a minimum amount to comment will force all comments, including livestreams, to have tips associated with them. This can help prevent spam.',
+ MIN_SUPER: 'Enabling a minimum amount to hyperchat will force all TIPPED comments to have this value in order to be shown. This still allows regular comments to be posted.',
+ MIN_SUPER_OFF: '(This settings is not applicable if all comments require a tip.)',
+ BLOCKED_WORDS: 'Comments and livestream chat containing these words will be blocked.',
+ MODERATORS: 'Moderators can block channels on your behalf. Blocked channels will appear in your "Blocked and Muted" list.',
+ MODERATOR_SEARCH: 'Enter a channel name or URL to add as a moderator.\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8',
+};
diff --git a/ui/page/settingsNotifications/view.jsx b/ui/page/settingsNotifications/view.jsx
index fa167cf86..6d5053034 100644
--- a/ui/page/settingsNotifications/view.jsx
+++ b/ui/page/settingsNotifications/view.jsx
@@ -6,6 +6,7 @@ import * as React from 'react';
import Page from 'component/page';
import { FormField } from 'component/common/form';
import Card from 'component/common/card';
+import SettingsRow from 'component/settingsRow';
import { Lbryio } from 'lbryinc';
import { useHistory } from 'react-router';
import { Redirect } from 'react-router-dom';
@@ -33,12 +34,12 @@ export default function NotificationSettingsPage(props: Props) {
React.useEffect(() => {
Lbryio.call('tag', 'list', lbryIoParams)
.then(setTags)
- .catch(e => {
+ .catch((e) => {
setError(true);
});
Lbryio.call('user_email', 'status', lbryIoParams)
- .then(res => {
+ .then((res) => {
const enabledEmails =
res.emails &&
Object.keys(res.emails).reduce((acc, email) => {
@@ -49,7 +50,7 @@ export default function NotificationSettingsPage(props: Props) {
setTagMap(res.tags);
setEnabledEmails(enabledEmails);
})
- .catch(e => {
+ .catch((e) => {
setError(true);
});
}, []);
@@ -73,7 +74,7 @@ export default function NotificationSettingsPage(props: Props) {
})
.then(() => {
const newEnabledEmails = enabledEmails
- ? enabledEmails.map(userEmail => {
+ ? enabledEmails.map((userEmail) => {
if (email === userEmail.email) {
return { email, isEnabled: newIsEnabled };
}
@@ -84,7 +85,7 @@ export default function NotificationSettingsPage(props: Props) {
setEnabledEmails(newEnabledEmails);
})
- .catch(e => {
+ .catch((e) => {
setError(true);
});
}
@@ -94,7 +95,13 @@ export default function NotificationSettingsPage(props: Props) {
}
return (
-
+
{error ? (
{/* @if TARGET='app' */}
+
+
{__('App notifications')}
+
{__('Notification settings for the desktop app.')}
+
setClientSetting(SETTINGS.OS_NOTIFICATIONS_ENABLED, !osNotificationsEnabled)}
- checked={osNotificationsEnabled}
- label={__('Show Desktop Notifications')}
- helper={__('Get notified when an upload or channel is confirmed.')}
- />
+ isBodyList
+ body={
+
+ setClientSetting(SETTINGS.OS_NOTIFICATIONS_ENABLED, !osNotificationsEnabled)}
+ checked={osNotificationsEnabled}
+ />
+
}
/>
-
{/* @endif */}
{enabledEmails && enabledEmails.length > 0 && (
-
- {enabledEmails.map(({ email, isEnabled }) => (
- handleChangeEmail(email, !isEnabled)}
- checked={isEnabled}
- label={email}
- />
- ))}
- >
- }
- />
+ <>
+
+
+ {enabledEmails.length === 1 ? __('Your email') : __('Receiving addresses')}
+
+
+ {__('Uncheck your email below if you want to stop receiving messages.')}
+
+
+
+ {enabledEmails.map(({ email, isEnabled }) => (
+
+ handleChangeEmail(email, !isEnabled)}
+ checked={isEnabled}
+ />
+
+ ))}
+ >
+ }
+ />
+ >
)}
{tags && tags.length > 0 && (
-
- {tags.map(tag => {
- const isEnabled = tagMap[tag.name];
- return (
- handleChangeTag(tag.name, !isEnabled)}
- checked={isEnabled}
- label={__(tag.description)}
- />
- );
- })}
- >
- }
- />
+ <>
+
+
{__('Email preferences')}
+
+ {__("Opt out of any topics you don't want to receive email about.")}
+
+
+
+ {tags.map((tag) => {
+ const isEnabled = tagMap[tag.name];
+ return (
+
+ handleChangeTag(tag.name, !isEnabled)}
+ checked={isEnabled}
+ />
+
+ );
+ })}
+ >
+ }
+ />
+ >
)}
)}
diff --git a/ui/page/settingsStripeAccount/view.jsx b/ui/page/settingsStripeAccount/view.jsx
index f91caaf57..fc63e9e42 100644
--- a/ui/page/settingsStripeAccount/view.jsx
+++ b/ui/page/settingsStripeAccount/view.jsx
@@ -194,7 +194,13 @@ class StripeAccountConnection extends React.Component {
} = this.state;
return (
-
+
{__('Connect a bank account')}}
isBodyList
diff --git a/ui/page/settingsStripeCard/view.jsx b/ui/page/settingsStripeCard/view.jsx
index 7c1903ab9..3b101d012 100644
--- a/ui/page/settingsStripeCard/view.jsx
+++ b/ui/page/settingsStripeCard/view.jsx
@@ -354,7 +354,13 @@ class SettingsStripeCard extends React.Component {
const { currentFlowStage, pageTitle, userCardDetails, paymentMethodId } = this.state;
return (
-
+
{scriptFailedToLoad && (
{__('There was an error connecting to Stripe. Please try again later.')}
diff --git a/web/scss/themes/odysee/dark.scss b/web/scss/themes/odysee/dark.scss
index 6cae5e194..ae5d31e53 100644
--- a/web/scss/themes/odysee/dark.scss
+++ b/web/scss/themes/odysee/dark.scss
@@ -26,12 +26,6 @@
--color-help-warning-text: var(--color-white-alt);
--color-help-warning-bg: #fbbf2450;
- // Tags (words)
- --color-tag-words: var(--color-text);
- --color-tag-words-bg: var(--color-gray-5);
- --color-tag-words-hover: var(--color-white);
- --color-tag-words-bg-hover: var(--color-gray-4);
-
// Header
--color-header-button: #38274c;
--color-header-background: #231830;
diff --git a/web/scss/themes/odysee/init/_base-theme.scss b/web/scss/themes/odysee/init/_base-theme.scss
index 76096b53c..f03b4a055 100644
--- a/web/scss/themes/odysee/init/_base-theme.scss
+++ b/web/scss/themes/odysee/init/_base-theme.scss
@@ -115,10 +115,10 @@
--color-tag-bg-hover: var(--color-button-primary-bg);
// Tags (words)
- --color-tag-words: var(--color-gray-5);
- --color-tag-words-bg: var(--color-button-alt-bg);
- --color-tag-words-hover: var(--color-button-alt-text);
- --color-tag-words-bg-hover: var(--color-button-alt-bg-hover);
+ --color-tag-words: var(--color-primary);
+ --color-tag-words-bg: var(--color-primary-alt);
+ --color-tag-words-hover: var(--color-primary);
+ --color-tag-words-bg-hover: var(--color-primary-alt-3);
// Menu
--color-menu-background: var(--color-header-background);
From 6d5c32ba2e98da7eefdb037a772e2227d762ffd2 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 19 Aug 2021 15:10:04 +0800
Subject: [PATCH 29/36] Help string style cleanup for consistency.
Changed from constants to object. This allows us to skip prettier's auto line-breaking with just one comment (instead of for each constant), plus I like object style to group things together in general.
---
ui/component/settingContent/view.jsx | 31 ++++++++++++++--------------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/ui/component/settingContent/view.jsx b/ui/component/settingContent/view.jsx
index 42c2dbd17..ccb9d92d3 100644
--- a/ui/component/settingContent/view.jsx
+++ b/ui/component/settingContent/view.jsx
@@ -61,7 +61,7 @@ export default function SettingContent(props: Props) {
isBodyList
body={
<>
-
+
-
+
-
+
*/}
-
+
{/* @if TARGET='app' */}
-
+
{/* @endif */}
@@ -186,7 +186,7 @@ export default function SettingContent(props: Props) {
name="instant_purchases"
checked={instantPurchaseEnabled}
label={__('Only confirm purchases or tips over a certain amount')}
- helper={__(HELP_ONLY_CONFIRM_OVER_AMOUNT)}
+ helper={__(HELP.ONLY_CONFIRM_OVER_AMOUNT)}
onChange={() => setClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, true)}
/>
{instantPurchaseEnabled && (
@@ -205,13 +205,12 @@ export default function SettingContent(props: Props) {
);
}
-const HELP_FLOATING_PLAYER = 'Keep content playing in the corner when navigating to a different page.';
-const HELP_AUTOPLAY =
- 'Autoplay video and audio files when navigating to a file, as well as the next related item when a file finishes playing.';
-const HELP_HIDE_REPOSTS = 'You will not see reposts by people you follow or receive email notifying about them.';
-const HELP_SHOW_MATURE =
- 'Mature content may include nudity, intense sexuality, profanity, or other adult content. By displaying mature content, you are affirming you are of legal age to view mature content in your country or jurisdiction. ';
-const HELP_MAX_PURCHASE_PRICE =
- 'This will prevent you from purchasing any content over a certain cost, as a safety measure.';
-const HELP_ONLY_CONFIRM_OVER_AMOUNT = ''; // feel redundant. Disable for now.
-// const HELP_ONLY_CONFIRM_OVER_AMOUNT = "When this option is chosen, LBRY won't ask you to confirm purchases or tips below your chosen amount.";
+// prettier-ignore
+const HELP = {
+ FLOATING_PLAYER: 'Keep content playing in the corner when navigating to a different page.',
+ AUTOPLAY: 'Autoplay video and audio files when navigating to a file, as well as the next related item when a file finishes playing.',
+ HIDE_REPOSTS: 'You will not see reposts by people you follow or receive email notifying about them.',
+ SHOW_MATURE: 'Mature content may include nudity, intense sexuality, profanity, or other adult content. By displaying mature content, you are affirming you are of legal age to view mature content in your country or jurisdiction. ',
+ MAX_PURCHASE_PRICE: 'This will prevent you from purchasing any content over a certain cost, as a safety measure.',
+ ONLY_CONFIRM_OVER_AMOUNT: '', // [feel redundant. Disable for now] "When this option is chosen, LBRY won't ask you to confirm purchases or tips below your chosen amount.",
+};
From 950518257692e12228c46b81481219a15712c936 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 19 Aug 2021 15:18:36 +0800
Subject: [PATCH 30/36] Use positive "Publish preview" text, per feedback
It was phrased negatively as the feedback back then was the string should match whatever is on the actual dialog (which was "Skip preview and confirmation"). But now it looks odd when we have an additional title string. We think titles should be positively phrased, hence the change.
---
static/app-strings.json | 1 +
ui/component/settingContent/view.jsx | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/static/app-strings.json b/static/app-strings.json
index ac86246da..5e7edab7f 100644
--- a/static/app-strings.json
+++ b/static/app-strings.json
@@ -1421,6 +1421,7 @@
"Est. transaction fee:": "Est. transaction fee:",
"Publish confirmation": "Publish confirmation",
"Skip preview and confirmation": "Skip preview and confirmation",
+ "Show preview and confirmation dialog before publishing content.": "Show preview and confirmation dialog before publishing content.",
"Upload settings": "Upload settings",
"Currently Uploading": "Currently Uploading",
"Leave the app running until upload is complete": "Leave the app running until upload is complete",
diff --git a/ui/component/settingContent/view.jsx b/ui/component/settingContent/view.jsx
index ccb9d92d3..671c21ff0 100644
--- a/ui/component/settingContent/view.jsx
+++ b/ui/component/settingContent/view.jsx
@@ -157,12 +157,12 @@ export default function SettingContent(props: Props) {
>
)}
-
+
setClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW, !enablePublishPreview)}
/>
@@ -213,4 +213,5 @@ const HELP = {
SHOW_MATURE: 'Mature content may include nudity, intense sexuality, profanity, or other adult content. By displaying mature content, you are affirming you are of legal age to view mature content in your country or jurisdiction. ',
MAX_PURCHASE_PRICE: 'This will prevent you from purchasing any content over a certain cost, as a safety measure.',
ONLY_CONFIRM_OVER_AMOUNT: '', // [feel redundant. Disable for now] "When this option is chosen, LBRY won't ask you to confirm purchases or tips below your chosen amount.",
+ PUBLISH_PREVIEW: 'Show preview and confirmation dialog before publishing content.',
};
From 6b82aae735d46dabb9b84a955c57583118410b89 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 19 Aug 2021 22:06:22 +0800
Subject: [PATCH 31/36] Change "Update password" to a secondary button
Per feedback.
---
ui/component/settingAccountPassword/view.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/component/settingAccountPassword/view.jsx b/ui/component/settingAccountPassword/view.jsx
index 494d078e6..b3498c652 100644
--- a/ui/component/settingAccountPassword/view.jsx
+++ b/ui/component/settingAccountPassword/view.jsx
@@ -79,7 +79,7 @@ export default function SettingAccountPassword(props: Props) {
{!hasPassword && {__('You do not currently have a password set.')}
}
setIsAddingPassword(true)}
/>
From 049a95276544bd8cffabed5ffd30197200aa34fe Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Thu, 19 Aug 2021 22:38:15 +0800
Subject: [PATCH 32/36] Change "Manage" from solid button to simple text.
I think this looks cleaner.
I've also tried just having a right solid chevron, but it doesn't look nice with other widgets like checkboxes.
---
ui/component/settingAccount/view.jsx | 8 ++++----
ui/component/settingContent/view.jsx | 12 ++++++------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/ui/component/settingAccount/view.jsx b/ui/component/settingAccount/view.jsx
index c0593a09b..b41538574 100644
--- a/ui/component/settingAccount/view.jsx
+++ b/ui/component/settingAccount/view.jsx
@@ -66,9 +66,9 @@ export default function SettingAccount(props: Props) {
subtitle={__('Connect a bank account to receive tips and compensation in your local currency')}
>
@@ -82,9 +82,9 @@ export default function SettingAccount(props: Props) {
subtitle={__('Add a credit card to tip creators in their local currency')}
>
diff --git a/ui/component/settingContent/view.jsx b/ui/component/settingContent/view.jsx
index 671c21ff0..4f3d287a7 100644
--- a/ui/component/settingContent/view.jsx
+++ b/ui/component/settingContent/view.jsx
@@ -128,18 +128,18 @@ export default function SettingContent(props: Props) {
<>
@@ -147,9 +147,9 @@ export default function SettingContent(props: Props) {
{myChannelUrls && myChannelUrls.length > 0 && (
From 741e8f96cbbc9f6ae364aa3fafc9ed886521e438 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Sun, 22 Aug 2021 14:14:46 +0800
Subject: [PATCH 33/36] "Back": change from "link" behavior to "up" behavior
The previous version treats each navigation as a link, ala Chrome Settings. I liked it because I can enter a settings section directly via URL, and can always back-track each section.
Anyway, changed it to the typical "up" behavior, traversing back the hierarchy of settings pages.
---
ui/component/settingsSideNavigation/view.jsx | 39 ++++++++++----------
1 file changed, 20 insertions(+), 19 deletions(-)
diff --git a/ui/component/settingsSideNavigation/view.jsx b/ui/component/settingsSideNavigation/view.jsx
index ad2d4a9df..e791e7ffb 100644
--- a/ui/component/settingsSideNavigation/view.jsx
+++ b/ui/component/settingsSideNavigation/view.jsx
@@ -25,25 +25,21 @@ type SideNavLink = {
const SIDE_LINKS: Array = [
{
title: 'Appearance',
- link: `/$/${PAGES.SETTINGS}#${SETTINGS_GRP.APPEARANCE}`,
section: SETTINGS_GRP.APPEARANCE,
icon: ICONS.APPEARANCE,
},
{
title: 'Account',
- link: `/$/${PAGES.SETTINGS}#${SETTINGS_GRP.ACCOUNT}`,
section: SETTINGS_GRP.ACCOUNT,
icon: ICONS.ACCOUNT,
},
{
title: 'Content settings',
- link: `/$/${PAGES.SETTINGS}#${SETTINGS_GRP.CONTENT}`,
section: SETTINGS_GRP.CONTENT,
icon: ICONS.CONTENT,
},
{
title: 'System',
- link: `/$/${PAGES.SETTINGS}#${SETTINGS_GRP.SYSTEM}`,
section: SETTINGS_GRP.SYSTEM,
icon: ICONS.SETTINGS,
},
@@ -54,12 +50,7 @@ export default function SettingsSideNavigation() {
const isMediumScreen = useIsMediumScreen();
const isAbsolute = isMediumScreen;
const microNavigation = !sidebarOpen || isMediumScreen;
- const { location } = useHistory();
-
- // This sidebar could be called from Settings or from a Settings Sub Page.
- // - "#" navigation = don't record to history, just scroll.
- // - "/" navigation = record sub-page navigation to history.
- const scrollInstead = location.pathname === `/$/${PAGES.SETTINGS}`;
+ const { location, goBack } = useHistory();
function scrollToSection(section: string) {
const TOP_MARGIN_PX = 20;
@@ -69,6 +60,21 @@ export default function SettingsSideNavigation() {
}
}
+ function getOnClickHandler(section) {
+ if (section) {
+ if (location.pathname === `/$/${PAGES.SETTINGS}`) {
+ return () => scrollToSection(section);
+ } else if (location.pathname.startsWith(`/$/${PAGES.SETTINGS}/`)) {
+ return () => {
+ goBack();
+ setTimeout(() => scrollToSection(section), 5);
+ };
+ }
+ }
+
+ return undefined;
+ }
+
if (isMediumScreen) {
// I think it's ok to hide it for now on medium/small screens given that
// we are using a scrolling Settings Page that displays everything. If we
@@ -97,16 +103,15 @@ export default function SettingsSideNavigation() {
{SIDE_LINKS.map((linkProps) => {
return (
- -
+
-
scrollToSection(linkProps.section) : undefined}
+ onClick={getOnClickHandler(linkProps.section)}
/>
{linkProps.extra && linkProps.extra}
@@ -131,18 +136,14 @@ export default function SettingsSideNavigation() {
// $FlowFixMe
const { link, route, ...passedProps } = linkProps;
return (
- -
+
-
scrollToSection(linkProps.section) : undefined
- }
+ onClick={getOnClickHandler(linkProps.section)}
/>
{linkProps.extra && linkProps.extra}
From b43ecd84661e576e4fca4d2c4cf865af0e21c925 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Sun, 22 Aug 2021 15:58:56 +0800
Subject: [PATCH 34/36] Switch from dual-pane to multirow
per feedback
---
static/app-strings.json | 7 ++-
ui/component/settingContent/view.jsx | 4 +-
ui/component/settingSystem/view.jsx | 70 +++++++++++++----------
ui/component/settingWalletServer/view.jsx | 10 ++--
4 files changed, 51 insertions(+), 40 deletions(-)
diff --git a/static/app-strings.json b/static/app-strings.json
index 5e7edab7f..f786b48e7 100644
--- a/static/app-strings.json
+++ b/static/app-strings.json
@@ -494,7 +494,7 @@
"Automatic dark mode": "Automatic dark mode",
"24-hour clock": "24-hour clock",
"Hide wallet balance in header": "Hide wallet balance in header",
- "Max Connections": "Max Connections",
+ "Max connections": "Max connections",
"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.": "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.",
"Show All": "Show All",
"LBRY names cannot contain spaces or reserved symbols": "LBRY names cannot contain spaces or reserved symbols",
@@ -512,7 +512,7 @@
"Read more": "Read more",
"Multi-language support is brand new and incomplete. Switching your language may have unintended consequences, like glossolalia.": "Multi-language support is brand new and incomplete. Switching your language may have unintended consequences, like glossolalia.",
"Tailor your experience.": "Tailor your experience.",
- "Save Password": "Save Password",
+ "Save wallet password": "Save wallet password",
"Automatically unlock your wallet on startup": "Automatically unlock your wallet on startup",
"Dark": "Dark",
"light": "light",
@@ -710,7 +710,8 @@
"Failed to copy RSS URL.": "Failed to copy RSS URL.",
"Text copied": "Text copied",
"Rewards Disabled": "Rewards Disabled",
- "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%.": "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%.",
+ "Wallet server": "Wallet server",
+ "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%": "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%",
"Your Tags": "Your Tags",
"All Content": "All Content",
"Accepted": "Accepted",
diff --git a/ui/component/settingContent/view.jsx b/ui/component/settingContent/view.jsx
index 4f3d287a7..11e770df7 100644
--- a/ui/component/settingContent/view.jsx
+++ b/ui/component/settingContent/view.jsx
@@ -168,12 +168,12 @@ export default function SettingContent(props: Props) {
{/* @if TARGET='app' */}
-
+
{/* @endif */}
-
+
}
- useVerticalSeparator
+ multirow
>
+
+
+ ),
+ }}
+ >
+ Wallet encryption is currently unavailable until it's supported for synced accounts. It will be
+ added back soon. %learn_more%.
+
+ {/* {__('Secure your local wallet data with a custom password.')}{' '}
+ {__('Lost passwords cannot be recovered.')}
+ . */}
+
+ }
+ >
onChangeEncryptWallet()}
checked={walletEncrypted}
- label={__('Encrypt my wallet with a custom password')}
- helper={
-
-
- ),
- }}
- >
- Wallet encryption is currently unavailable until it's supported for synced accounts. It will be
- added back soon. %learn_more%.
-
- {/* {__('Secure your local wallet data with a custom password.')}{' '}
- {__('Lost passwords cannot be recovered.')}
- . */}
-
- }
/>
+
- {walletEncrypted && storedPassword && (
+ {walletEncrypted && storedPassword && (
+
{__('Automatically unlock your wallet on startup')}}
/>
- )}
-
+
+ )}
{/* @endif */}
{/* @if TARGET='app' */}
-
+
{/* Disabling below until we get downloads to work with shared subscriptions code */}
{/*
setDaemonSetting('max_connections_per_download', e.target.value)}
@@ -371,8 +376,13 @@ export default function SettingSystem(props: Props) {
))}
+
+
+
+
+
{/* @endif */}
diff --git a/ui/component/settingWalletServer/view.jsx b/ui/component/settingWalletServer/view.jsx
index 2d96cdf39..3f2b962d4 100644
--- a/ui/component/settingWalletServer/view.jsx
+++ b/ui/component/settingWalletServer/view.jsx
@@ -24,7 +24,7 @@ type DaemonStatus = {
type Props = {
getDaemonStatus: () => void,
- setCustomWalletServers: any => void,
+ setCustomWalletServers: (any) => void,
clearWalletServers: () => void,
customWalletServers: ServerConfig,
saveServerConfig: (Array) => void,
@@ -115,7 +115,7 @@ function SettingWalletServer(props: Props) {
name="default_wallet_servers"
checked={!advancedMode}
label={__('Use official lbry.tv wallet servers')}
- onChange={e => {
+ onChange={(e) => {
if (e.target.checked) {
doClear();
}
@@ -125,7 +125,7 @@ function SettingWalletServer(props: Props) {
type="radio"
name="custom_wallet_servers"
checked={advancedMode}
- onChange={e => {
+ onChange={(e) => {
setAdvancedMode(e.target.checked);
if (e.target.checked && customWalletServers.length) {
setCustomWalletServers(stringifyServerParam(customWalletServers));
@@ -140,7 +140,7 @@ function SettingWalletServer(props: Props) {
}}
>
Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content
- shows in trending or is blocked. %learn_more%.
+ shows in trending or is blocked. %learn_more%
@@ -150,7 +150,7 @@ function SettingWalletServer(props: Props) {
serverConfig.map((entry, index) => {
const [host, port] = entry;
const available = activeWalletServers.some(
- s => s.host === entry[0] && String(s.port) === entry[1] && s.availability
+ (s) => s.host === entry[0] && String(s.port) === entry[1] && s.availability
);
return (
From 3057f70c1c189861bbe94ff0bde92075a855a551 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Mon, 23 Aug 2021 14:18:27 +0800
Subject: [PATCH 35/36] Move 'update password' into a subpage
---
ui/component/router/view.jsx | 2 ++
ui/component/settingAccount/view.jsx | 12 +++++---
ui/component/settingAccountPassword/view.jsx | 31 ++++++++------------
ui/constants/pages.js | 1 +
ui/page/passwordUpdate/index.js | 3 ++
ui/page/passwordUpdate/view.jsx | 13 ++++++++
6 files changed, 39 insertions(+), 23 deletions(-)
create mode 100644 ui/page/passwordUpdate/index.js
create mode 100644 ui/page/passwordUpdate/view.jsx
diff --git a/ui/component/router/view.jsx b/ui/component/router/view.jsx
index fb32391a1..eb6f74fcf 100644
--- a/ui/component/router/view.jsx
+++ b/ui/component/router/view.jsx
@@ -82,6 +82,7 @@ const TagsFollowingManagePage = lazyImport(() =>
);
const TagsFollowingPage = lazyImport(() => import('page/tagsFollowing' /* webpackChunkName: "secondary" */));
const TopPage = lazyImport(() => import('page/top' /* webpackChunkName: "secondary" */));
+const UpdatePasswordPage = lazyImport(() => import('page/passwordUpdate' /* webpackChunkName: "passwordUpdate" */));
const Welcome = lazyImport(() => import('page/welcome' /* webpackChunkName: "secondary" */));
const YoutubeSyncPage = lazyImport(() => import('page/youtubeSync' /* webpackChunkName: "secondary" */));
@@ -294,6 +295,7 @@ function AppRouter(props: Props) {
+
{isAuthenticated && (
-
-
-
+
+
+
)}
{/* @if TARGET='app' */}
diff --git a/ui/component/settingAccountPassword/view.jsx b/ui/component/settingAccountPassword/view.jsx
index b3498c652..cc9561f15 100644
--- a/ui/component/settingAccountPassword/view.jsx
+++ b/ui/component/settingAccountPassword/view.jsx
@@ -1,8 +1,10 @@
// @flow
import React, { useState } from 'react';
+import { useHistory } from 'react-router';
import { FormField, Form } from 'component/common/form';
import Button from 'component/button';
import ErrorText from 'component/common/error-text';
+import SettingsRow from 'component/settingsRow';
import * as PAGES from 'constants/pages';
type Props = {
@@ -18,8 +20,11 @@ export default function SettingAccountPassword(props: Props) {
const { user, doToast, doUserPasswordSet, passwordSetSuccess, passwordSetError, doClearPasswordEntry } = props;
const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
- const [isAddingPassword, setIsAddingPassword] = useState(false);
const hasPassword = user && user.password_set;
+ const { goBack } = useHistory();
+
+ const title = hasPassword ? __('Update Your Password') : __('Add A Password');
+ const subtitle = hasPassword ? '' : __('You do not currently have a password set.');
function handleSubmit() {
doUserPasswordSet(newPassword, oldPassword);
@@ -27,7 +32,7 @@ export default function SettingAccountPassword(props: Props) {
React.useEffect(() => {
if (passwordSetSuccess) {
- setIsAddingPassword(false);
+ goBack();
doToast({
message: __('Password updated successfully.'),
});
@@ -35,10 +40,10 @@ export default function SettingAccountPassword(props: Props) {
setOldPassword('');
setNewPassword('');
}
- }, [passwordSetSuccess, setOldPassword, setNewPassword, doClearPasswordEntry, doToast]);
+ }, [passwordSetSuccess, setOldPassword, setNewPassword, doClearPasswordEntry, doToast, goBack]);
- return isAddingPassword ? (
-
+ return (
+
@@ -71,18 +76,6 @@ export default function SettingAccountPassword(props: Props) {
{passwordSetError}
)}
-
- ) : (
-
-
-
{__('Password')}
- {!hasPassword &&
{__('You do not currently have a password set.')}
}
-
-
setIsAddingPassword(true)}
- />
-
+
);
}
diff --git a/ui/constants/pages.js b/ui/constants/pages.js
index 8e5a221ae..eb1d836ec 100644
--- a/ui/constants/pages.js
+++ b/ui/constants/pages.js
@@ -43,6 +43,7 @@ exports.SETTINGS_STRIPE_ACCOUNT = 'settings/tip_account';
exports.SETTINGS_NOTIFICATIONS = 'settings/notifications';
exports.SETTINGS_BLOCKED_MUTED = 'settings/block_and_mute';
exports.SETTINGS_CREATOR = 'settings/creator';
+exports.SETTINGS_UPDATE_PWD = 'settings/update_password';
exports.SHOW = 'show';
exports.ACCOUNT = 'account';
exports.SEARCH = 'search';
diff --git a/ui/page/passwordUpdate/index.js b/ui/page/passwordUpdate/index.js
new file mode 100644
index 000000000..40e233027
--- /dev/null
+++ b/ui/page/passwordUpdate/index.js
@@ -0,0 +1,3 @@
+import PasswordUpdate from './view';
+
+export default PasswordUpdate;
diff --git a/ui/page/passwordUpdate/view.jsx b/ui/page/passwordUpdate/view.jsx
new file mode 100644
index 000000000..380f30095
--- /dev/null
+++ b/ui/page/passwordUpdate/view.jsx
@@ -0,0 +1,13 @@
+// @flow
+import React from 'react';
+import Card from 'component/common/card';
+import Page from 'component/page';
+import SettingAccountPassword from 'component/settingAccountPassword';
+
+export default function PasswordUpdate() {
+ return (
+
+ } />
+
+ );
+}
From 2e3d9cb609fb8729904e4620ad8ca5874cced3a0 Mon Sep 17 00:00:00 2001
From: infinite-persistence
Date: Mon, 23 Aug 2021 14:50:25 +0800
Subject: [PATCH 36/36] Update 'SyncToggle' to the new Settings style.
---
ui/component/settingAccount/view.jsx | 4 +---
ui/component/syncToggle/view.jsx | 10 +++++++---
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/ui/component/settingAccount/view.jsx b/ui/component/settingAccount/view.jsx
index 2e7e0494b..bca8861b1 100644
--- a/ui/component/settingAccount/view.jsx
+++ b/ui/component/settingAccount/view.jsx
@@ -58,9 +58,7 @@ export default function SettingAccount(props: Props) {
)}
{/* @if TARGET='app' */}
-
-
-
+
{/* @endif */}
{/* @if TARGET='web' */}
diff --git a/ui/component/syncToggle/view.jsx b/ui/component/syncToggle/view.jsx
index e6a702947..c795ed1b7 100644
--- a/ui/component/syncToggle/view.jsx
+++ b/ui/component/syncToggle/view.jsx
@@ -2,6 +2,7 @@
import * as MODALS from 'constants/modal_types';
import React from 'react';
import Button from 'component/button';
+import SettingsRow from 'component/settingsRow';
import { withRouter } from 'react-router';
import { FormField } from 'component/common/form';
@@ -20,11 +21,14 @@ function SyncToggle(props: Props) {
const { verifiedEmail, openModal, syncEnabled, disabled } = props;
return (
-
+
openModal(MODALS.SYNC_ENABLE, { mode: syncEnabled ? 'disable' : 'enable' })}
disabled={disabled || !verifiedEmail}
@@ -40,7 +44,7 @@ function SyncToggle(props: Props) {
)}
-
+
);
}