diff --git a/static/app-strings.json b/static/app-strings.json
index fb7124da6..b153fbec6 100644
--- a/static/app-strings.json
+++ b/static/app-strings.json
@@ -916,6 +916,17 @@
"Loading 3D model.": "Loading 3D model.",
"Click here": "Click here",
"PDF opened externally. %click_here% to open it again.": "PDF opened externally. %click_here% to open it again.",
+ "Wallet Server": "Wallet Server",
+ "lbry.tv wallet servers": "lbry.tv wallet servers",
+ "Custom wallet servers": "Custom wallet servers",
+ "Choose a different provider's wallet server.": "Choose a different provider's wallet server.",
+ "Host": "Host",
+ "Port": "Port",
+ "Available": "Available",
+ "Unable to load your saved preferences.": "Unable to load your saved preferences.",
+ "Add/Delete": "Add/Delete",
+ "The wallet server took a bit too long. Resetting defaults just in case.": "The wallet server took a bit too long. Resetting defaults just in case.",
+ "PDF opened externally. %click_here% to open it again.": "PDF opened externally. %click_here% to open it again.",
"%numberOfMonthsSincePublish% years ago": "%numberOfMonthsSincePublish% years ago",
"%numberOfYearsSincePublish% years ago": "%numberOfYearsSincePublish% years ago",
"%numberOfYearsSincePublish% year ago": "%numberOfYearsSincePublish% year ago",
@@ -925,4 +936,4 @@
"%numberOfMonthsSincePublish% months ago": "%numberOfMonthsSincePublish% months ago",
"%numberOfDaysSincePublish% months ago": "%numberOfDaysSincePublish% months ago",
"%numberOfDaysSincePublish% days ago": "%numberOfDaysSincePublish% days ago"
-}
\ No newline at end of file
+}
diff --git a/ui/component/app/index.js b/ui/component/app/index.js
index 0ffc13033..33cc17d4a 100644
--- a/ui/component/app/index.js
+++ b/ui/component/app/index.js
@@ -6,7 +6,7 @@ import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
import { doSetLanguage } from 'redux/actions/settings';
-import { doDownloadUpgradeRequested, doSignIn, doSyncWithPreferences } from 'redux/actions/app';
+import { doDownloadUpgradeRequested, doSignIn, doSyncWithPreferences, doGetAndPopulatePreferences } from 'redux/actions/app';
import App from './view';
const select = state => ({
@@ -30,6 +30,7 @@ const perform = dispatch => ({
signIn: () => dispatch(doSignIn()),
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
checkSync: () => dispatch(doSyncWithPreferences()),
+ updatePreferences: () => dispatch(doGetAndPopulatePreferences()),
});
export default hot(
diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx
index 14e51631e..de6b220c3 100644
--- a/ui/component/app/view.jsx
+++ b/ui/component/app/view.jsx
@@ -46,6 +46,7 @@ type Props = {
isUpgradeAvailable: boolean,
autoUpdateDownloaded: boolean,
checkSync: () => void,
+ updatePreferences: () => void,
syncEnabled: boolean,
uploadCount: number,
balance: ?number,
@@ -73,6 +74,7 @@ function App(props: Props) {
language,
languages,
setLanguage,
+ updatePreferences,
} = props;
const appRef = useRef();
@@ -168,6 +170,12 @@ function App(props: Props) {
}
}, [hasVerifiedEmail, syncEnabled, checkSync]);
+ useEffect(() => {
+ if (hasVerifiedEmail === false) {
+ updatePreferences();
+ }
+ }, [hasVerifiedEmail]);
+
useEffect(() => {
if (syncError) {
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
diff --git a/ui/component/settingWalletServer/index.js b/ui/component/settingWalletServer/index.js
new file mode 100644
index 000000000..79d9e4b19
--- /dev/null
+++ b/ui/component/settingWalletServer/index.js
@@ -0,0 +1,24 @@
+import { connect } from 'react-redux';
+import { DAEMON_SETTINGS } from 'lbry-redux';
+import { doSetDaemonSetting, doClearDaemonSetting, doGetDaemonStatus, doCacheCustomWalletServers, doFetchDaemonSettings } from 'redux/actions/settings';
+import { selectDaemonSettings, selectCachedWalletServers, makeSelectSharedPrefsForKey } from 'redux/selectors/settings';
+import SettingWalletServer from './view';
+
+const select = state => ({
+ daemonSettings: selectDaemonSettings(state),
+ customServers: selectCachedWalletServers(state),
+ serverPrefs: makeSelectSharedPrefsForKey(DAEMON_SETTINGS.LBRYUM_SERVERS)(state),
+});
+
+const perform = dispatch => ({
+ setWalletServers: (value) => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.LBRYUM_SERVERS, value)),
+ clearWalletServers: () => dispatch(doClearDaemonSetting(DAEMON_SETTINGS.LBRYUM_SERVERS)),
+ getDaemonStatus: () => dispatch(doGetDaemonStatus()),
+ saveServers: (servers) => dispatch(doCacheCustomWalletServers(servers)),
+ fetchDaemonSettings: () => dispatch(doFetchDaemonSettings()),
+});
+
+export default connect(
+ select,
+ perform
+)(SettingWalletServer);
diff --git a/ui/component/settingWalletServer/internal/displayRow.jsx b/ui/component/settingWalletServer/internal/displayRow.jsx
new file mode 100644
index 000000000..da17294d8
--- /dev/null
+++ b/ui/component/settingWalletServer/internal/displayRow.jsx
@@ -0,0 +1,35 @@
+// @flow
+import * as ICONS from 'constants/icons';
+import React from 'react';
+import Button from 'component/button';
+import Icon from 'component/common/icon';
+
+type Props = {
+ host: string,
+ port: string,
+ available: boolean,
+ index: number,
+ remove: number => void,
+};
+
+function ServerDisplayRow(props: Props) {
+ const { host, port, available, index, remove } = props;
+ return (
+
+
+ {host}
+ |
+
+ {port}
+ |
+
+ {available && }
+ |
+
+ |
+
+ );
+}
+
+export default ServerDisplayRow;
diff --git a/ui/component/settingWalletServer/internal/inputRow.jsx b/ui/component/settingWalletServer/internal/inputRow.jsx
new file mode 100644
index 000000000..53171f046
--- /dev/null
+++ b/ui/component/settingWalletServer/internal/inputRow.jsx
@@ -0,0 +1,47 @@
+// @flow
+import * as ICONS from 'constants/icons';
+import React, { useState, useEffect } from 'react';
+import Button from 'component/button';
+import { FormField } from 'component/common/form';
+
+type Props = {
+ update: (string) => void,
+};
+
+function ServerInputRow(props: Props) {
+ const { update } = props;
+ const ValidIpAddressRegex = new RegExp('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\.)){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$');
+ const ValidHostnameRegex = new RegExp('^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])(\\.))+([A-Za-z]|[A-Za-z][A-Za-z]*[A-Za-z])$');
+ const ValidPortRegex = new RegExp('^([0-9]){1,5}$');
+
+ const [hostString, setHostString] = useState('');
+ const [portString, setPortString] = useState('');
+ const [valid, setValid] = useState(false);
+
+ useEffect(() => {
+ setValid((ValidIpAddressRegex.test(hostString) || ValidHostnameRegex.test(hostString)) && ValidPortRegex.test(portString));
+ }, [hostString, portString, valid, setValid]);
+
+ function onClick() {
+ update([hostString, portString]);
+ setHostString('');
+ setPortString('');
+ }
+
+ return (
+
+ {/* host */}
+ setHostString(e.target.value)}/>
+ |
+ {/* port */}
+ setPortString(e.target.value)}/>
+ |
+ |
+
+ |
+
+ );
+}
+
+export default ServerInputRow;
diff --git a/ui/component/settingWalletServer/view.jsx b/ui/component/settingWalletServer/view.jsx
new file mode 100644
index 000000000..af768502f
--- /dev/null
+++ b/ui/component/settingWalletServer/view.jsx
@@ -0,0 +1,174 @@
+// @flow
+
+import React, { useState, useEffect } from 'react';
+import { FormField } from 'component/common/form';
+import ServerInputRow from './internal/inputRow';
+import ServerDisplayRow from './internal/displayRow';
+
+type DaemonSettings = {
+ lbryum_servers: Array<>
+};
+
+type StatusOfServer = {
+ host: string,
+ port: string,
+ availability: boolean,
+ latency: number,
+}
+
+type ServerInConfig = Array // ['host', 'port']
+
+type DisplayOfServer = {
+ host: string,
+ port: string,
+ availability: boolean,
+}
+
+type ServerStatus = Array
+type ServerConfig = Array
+type DisplayList = Array
+
+type Props = {
+ daemonSettings: DaemonSettings,
+ getDaemonStatus: () => void,
+ setWalletServers: any => void,
+ clearWalletServers: () => void,
+ customServers: ServerConfig,
+ saveServers: string => void,
+ fetchDaemonSettings: () => void,
+ serverPrefs: ServerConfig,
+};
+
+function SettingWalletServer(props: Props) {
+ const {
+ daemonSettings,
+ fetchDaemonSettings,
+ setWalletServers,
+ getDaemonStatus,
+ clearWalletServers,
+ saveServers,
+ customServers,
+ serverPrefs,
+ } = props;
+
+ const [custom, setCustom] = useState(false);
+ const [status, setStatus] = useState([]);
+ const serversInConfig: ServerConfig = daemonSettings && daemonSettings.lbryum_servers;
+ const servers = customServers.length ? customServers : serversInConfig;
+ const STATUS_INTERVAL = 5000;
+
+ useEffect(() => {
+ if (serverPrefs && serverPrefs.length) {
+ setCustom(true);
+ }
+ }, []);
+
+ // TODO: do this globally to have status updated for the app
+ useEffect(() => {
+ const interval = setInterval(() => {
+ getDaemonStatus()
+ .then(s => {
+ if (s && s.wallet && s.wallet.servers) {
+ setStatus(s.wallet.servers);
+ }
+ });
+ }, STATUS_INTERVAL);
+ return () => clearInterval(interval);
+ }, []);
+
+ useEffect(() => {
+ fetchDaemonSettings();
+ }, [custom]);
+
+ function makeDisplayList(l) {
+ const displayList = [];
+ l.forEach(entry => {
+ displayList.push({
+ host: entry[0],
+ port: entry[1],
+ available:
+ (status && status.some(s => s.host === entry[0] && String(s.port) === entry[1] && s.availability)) ||
+ false,
+ });
+ });
+ return displayList;
+ }
+
+ function makeServerParam(configList) {
+ return configList.reduce((acc, cur) => {
+ acc.push(`${cur[0]}:${cur[1]}`);
+ return acc;
+ }, []);
+ }
+
+ function doClear() {
+ setCustom(false);
+ clearWalletServers();
+ }
+
+ function onAdd(serverTuple) {
+ let newServerConfig = servers.concat();
+ newServerConfig.push(serverTuple);
+ saveServers(newServerConfig);
+ setWalletServers(makeServerParam(newServerConfig));
+ }
+
+ function onDelete(i) {
+ const newServerList = servers.concat();
+ newServerList.splice(i, 1);
+ saveServers(newServerList);
+ setWalletServers(makeServerParam(newServerList));
+ }
+
+ return (
+
+ {
+ if (e.target.checked) {
+ doClear();
+ }
+ }}
+ />
+ {
+ setCustom(e.target.checked);
+ if (e.target.checked) {
+ setWalletServers(makeServerParam(customServers));
+ }
+ }}
+ label={__('Custom wallet servers')}
+ />
+ {custom && (
+
+
+
+
+ {__('Host')} |
+ {__('Port')} |
+ {__('Available')} |
+ {__('Add/Delete')} |
+
+
+
+ {servers &&
+ makeDisplayList(servers).map((t, i) => (
+
+ ))}
+
+
+
+
+ )}
+ {__(`Choose a different provider's wallet server.`)}
+
+ );
+}
+
+export default SettingWalletServer;
diff --git a/ui/component/splash/index.js b/ui/component/splash/index.js
index 13d22772f..5f8788e00 100644
--- a/ui/component/splash/index.js
+++ b/ui/component/splash/index.js
@@ -2,8 +2,9 @@ import * as MODALS from 'constants/modal_types';
import { connect } from 'react-redux';
import { selectDaemonVersionMatched, selectModal } from 'redux/selectors/app';
import { doCheckDaemonVersion, doOpenModal, doHideModal } from 'redux/actions/app';
-import { doSetClientSetting } from 'redux/actions/settings';
+import { doSetClientSetting, doClearDaemonSetting } from 'redux/actions/settings';
import * as settings from 'constants/settings';
+import { doToast, DAEMON_SETTINGS } from 'lbry-redux';
import SplashScreen from './view';
import { makeSelectClientSetting } from 'redux/selectors/settings';
@@ -20,6 +21,8 @@ const perform = dispatch => ({
dispatch(doOpenModal(MODALS.WALLET_UNLOCK, { shouldTryWithBlankPassword })),
hideModal: () => dispatch(doHideModal()),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
+ clearWalletServers: () => dispatch(doClearDaemonSetting(DAEMON_SETTINGS.LBRYUM_SERVERS)),
+ doShowSnackBar: message => dispatch(doToast({ isError: true, message })),
});
export default connect(
diff --git a/ui/component/splash/view.jsx b/ui/component/splash/view.jsx
index e4a3ba2d2..d6204d359 100644
--- a/ui/component/splash/view.jsx
+++ b/ui/component/splash/view.jsx
@@ -13,6 +13,8 @@ import I18nMessage from 'component/i18nMessage';
import 'css-doodle';
const FORTY_FIVE_SECONDS = 45 * 1000;
+const UPDATE_INTERVAL = 500; // .5 seconds
+const MAX_WALLET_WAIT = 10; // 10 seconds for wallet to be started, but servers to be unavailable
type Props = {
checkDaemonVersion: () => Promise,
@@ -26,6 +28,8 @@ type Props = {
},
animationHidden: boolean,
setClientSetting: (string, boolean) => void,
+ clearWalletServers: () => void,
+ doShowSnackBar: string => void,
};
type State = {
@@ -35,6 +39,10 @@ type State = {
error: boolean,
isRunning: boolean,
launchWithIncompatibleDaemon: boolean,
+ walletServerAvailable: boolean,
+ waitingForWallet: number,
+ hasPopulated: boolean,
+ hasReconnected: boolean,
};
export default class SplashScreen extends React.PureComponent {
@@ -48,6 +56,7 @@ export default class SplashScreen extends React.PureComponent {
error: false,
launchWithIncompatibleDaemon: false,
isRunning: false,
+ waitingForWallet: 0,
};
(this: any).renderModals = this.renderModals.bind(this);
@@ -98,7 +107,7 @@ export default class SplashScreen extends React.PureComponent {
}
updateStatus() {
- const { modal, notifyUnlockWallet } = this.props;
+ const { modal, notifyUnlockWallet, clearWalletServers, doShowSnackBar } = this.props;
const { launchedModal } = this.state;
Lbry.status().then(status => {
@@ -118,6 +127,11 @@ export default class SplashScreen extends React.PureComponent {
this.updateStatusCallback(status);
}
});
+ } else if (this.state.waitingForWallet > MAX_WALLET_WAIT && launchedModal === false && !modal) {
+ clearWalletServers();
+ doShowSnackBar(__('The wallet server took a bit too long. Resetting defaults just in case.'))
+ this.setState({waitingForWallet: 0});
+ this.updateStatusCallback(status)
} else {
this.updateStatusCallback(status);
}
@@ -146,7 +160,9 @@ export default class SplashScreen extends React.PureComponent {
});
return;
- } else if (wallet && wallet.blocks_behind > 0) {
+ } else if (startupStatus && !startupStatus.wallet && wallet.available_servers < 1) {
+ this.setState({waitingForWallet: this.state.waitingForWallet + (UPDATE_INTERVAL / 1000)});
+ } else if (wallet && wallet.blocks_behind > 0) {
this.setState({
message: __('Blockchain Sync'),
details: `${__('Catching up...')} (${wallet.headers_synchronization_progress}%)`,
@@ -160,7 +176,7 @@ export default class SplashScreen extends React.PureComponent {
setTimeout(() => {
this.updateStatus();
- }, 500);
+ }, UPDATE_INTERVAL);
}
runWithIncompatibleDaemon() {
diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js
index fb7cfe522..1486d0a55 100644
--- a/ui/constants/action_types.js
+++ b/ui/constants/action_types.js
@@ -114,6 +114,7 @@ export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
+export const WALLET_SERVERS_CACHED = 'WALLET_SERVERS_CACHED';
// User
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
diff --git a/ui/page/settings/view.jsx b/ui/page/settings/view.jsx
index dfbcff5e2..4dd6cb014 100644
--- a/ui/page/settings/view.jsx
+++ b/ui/page/settings/view.jsx
@@ -11,6 +11,7 @@ import Button from 'component/button';
import I18nMessage from 'component/i18nMessage';
import Page from 'component/page';
import SettingLanguage from 'component/settingLanguage';
+import SettingWalletServer from 'component/settingWalletServer';
import SettingAutoLaunch from 'component/settingAutoLaunch';
import FileSelector from 'component/common/file-selector';
import SyncToggle from 'component/syncToggle';
@@ -225,6 +226,7 @@ class SettingsPage extends React.PureComponent {
} />
{/* @if TARGET='app' */}
+ } />
{
+ function successCb(savedPreferences) {
+ if (savedPreferences !== null) {
+ dispatch(doPopulateSharedUserState(savedPreferences));
+ }
+ }
+
+ function failCb() {
+ deleteSavedPassword().then(() => {
+ dispatch(
+ doToast({
+ isError: true,
+ message: __('Unable to load your saved preferences.'),
+ })
+ );
+ });
+ }
+ doPreferenceGet('shared', successCb, failCb);
+ };
+}
+
export function doSyncWithPreferences() {
return dispatch => {
function handleSyncComplete() {
+ // we just got sync data, better update our channels
dispatch(doFetchChannelListMine());
-
- function successCb(savedPreferences) {
- if (savedPreferences !== null) {
- dispatch(doPopulateSharedUserState(savedPreferences));
- }
- }
-
- function failCb() {
- deleteSavedPassword().then(() => {
- dispatch(
- doToast({
- isError: true,
- message: __('Unable to load your saved preferences.'),
- })
- );
- });
- }
-
- doPreferenceGet('shared', successCb, failCb);
+ dispatch(doGetAndPopulatePreferences());
}
return getSavedPassword().then(password => {
diff --git a/ui/redux/actions/settings.js b/ui/redux/actions/settings.js
index 3c85bd446..dd17516cc 100644
--- a/ui/redux/actions/settings.js
+++ b/ui/redux/actions/settings.js
@@ -1,4 +1,4 @@
-import { Lbry, ACTIONS, doToast } from 'lbry-redux';
+import { Lbry, ACTIONS, doToast, SHARED_PREFS, doWalletReconnect } from 'lbry-redux';
import * as SETTINGS from 'constants/settings';
import * as LOCAL_ACTIONS from 'constants/action_types';
import analytics from 'analytics';
@@ -23,13 +23,22 @@ export function doFetchDaemonSettings() {
};
}
-export function doSetDaemonSetting(key, value) {
+export function doClearDaemonSetting(key) {
return dispatch => {
- const newSettings = {
+ const clearKey = {
key,
- value: !value && value !== false ? null : value,
};
- Lbry.settings_set(newSettings).then(newSettings);
+ Lbry.settings_clear(clearKey).then(defaultSettings => {
+ if (Object.values(SHARED_PREFS).includes(key)) {
+ dispatch({
+ type: ACTIONS.SHARED_PREFERENCE_SET,
+ data: { key: key, value: undefined },
+ });
+ }
+ if (key === SHARED_PREFS.WALLET_SERVERS) {
+ dispatch(doWalletReconnect());
+ }
+ });
Lbry.settings_get().then(settings => {
analytics.toggle(settings.share_usage_data);
dispatch({
@@ -42,6 +51,49 @@ export function doSetDaemonSetting(key, value) {
};
}
+export function doSetDaemonSetting(key, value) {
+ return dispatch => {
+ const newSettings = {
+ key,
+ value: !value && value !== false ? null : value,
+ };
+ Lbry.settings_set(newSettings).then(newSetting => {
+ if (Object.values(SHARED_PREFS).includes(key)) {
+ dispatch({
+ type: ACTIONS.SHARED_PREFERENCE_SET,
+ data: {key: key, value: newSetting[key]},
+ });
+ }
+ // hardcoding this in lieu of a better solution
+ if (key === SHARED_PREFS.WALLET_SERVERS) {
+ dispatch(doWalletReconnect());
+ }
+ });
+ Lbry.settings_get().then(settings => {
+ analytics.toggle(settings.share_usage_data);
+ dispatch({
+ type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
+ data: {
+ settings,
+ },
+ });
+ });
+ };
+}
+
+export function doCacheCustomWalletServers(servers) {
+ return {
+ type: ACTIONS.WALLET_SERVERS_CACHED,
+ data: servers,
+ };
+}
+
+export function doGetDaemonStatus() {
+ return dispatch => {
+ return Lbry.status().then(settings => settings);
+ };
+};
+
export function doSetClientSetting(key, value) {
return {
type: ACTIONS.CLIENT_SETTING_CHANGED,
diff --git a/ui/redux/reducers/settings.js b/ui/redux/reducers/settings.js
index 714b3f7cb..bcc8acd3e 100644
--- a/ui/redux/reducers/settings.js
+++ b/ui/redux/reducers/settings.js
@@ -2,11 +2,16 @@ import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings';
import moment from 'moment';
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
+import { ACTIONS as LBRY_REDUX_ACTIONS, SHARED_PREFS } from 'lbry-redux';
const reducers = {};
const defaultState = {
isNight: false,
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
+ customWalletServers: [],
+ sharedPrefs: {
+ [SHARED_PREFS.WALLET_SERVERS]: false,
+ },
daemonSettings: {},
clientSettings: {
// UX
@@ -92,6 +97,41 @@ reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCESS] = (state, action) => {
}
};
+reducers[LBRY_REDUX_ACTIONS.SHARED_PREFERENCE_SET] = (state, action) => {
+ const { key, value } = action.data;
+ const sharedPrefs = Object.assign({}, state.sharedPrefs);
+ sharedPrefs[key] = value;
+
+ return Object.assign({}, state, {
+ sharedPrefs,
+ });
+}
+
+reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
+ const { key, value } = action.data;
+ const clientSettings = Object.assign({}, state.clientSettings);
+
+ clientSettings[key] = value;
+
+ return Object.assign({}, state, {
+ clientSettings,
+ });
+};
+
+reducers[LBRY_REDUX_ACTIONS.USER_STATE_POPULATE] = (
+ state,
+ action,
+) => {
+ const { settings: sharedPrefs } = action.data;
+
+ // process clientSettings and daemonSettings
+ return Object.assign({}, state, { sharedPrefs });
+};
+
+reducers[ACTIONS.WALLET_SERVERS_CACHED] = (state, action) => {
+ return Object.assign({}, state, {customWalletServers: action.data});
+}
+
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/redux/selectors/settings.js b/ui/redux/selectors/settings.js
index eba15b952..dca171e98 100644
--- a/ui/redux/selectors/settings.js
+++ b/ui/redux/selectors/settings.js
@@ -34,6 +34,22 @@ export const selectIsNight = createSelector(
state => state.isNight
);
+export const selectCachedWalletServers = createSelector(
+ selectState,
+ state => state.customWalletServers
+);
+
+export const selectSharedPrefs = createSelector(
+ selectState,
+ state => state.sharedPrefs
+);
+
+export const makeSelectSharedPrefsForKey = key =>
+ createSelector(
+ selectSharedPrefs,
+ prefs => (prefs ? prefs[key] : undefined)
+ );
+
export const selectThemePath = createSelector(
selectTheme,
selectAutomaticDarkModeEnabled,
diff --git a/ui/store.js b/ui/store.js
index 4e7fbb6a4..d6313ae2d 100644
--- a/ui/store.js
+++ b/ui/store.js
@@ -108,6 +108,7 @@ const triggerSharedStateActions = [
LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW,
LBRY_REDUX_ACTIONS.TOGGLE_BLOCK_CHANNEL,
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
+ LBRY_REDUX_ACTIONS.SHARED_PREFERENCE_SET,
];
/**
@@ -129,6 +130,7 @@ const sharedStateFilters = {
},
},
blocked: { source: 'blocked', property: 'blockedChannels' },
+ settings: { source: 'settings', property: 'sharedPrefs'},
};
const sharedStateCb = ({ dispatch, getState }) => {