enable wallet server management and handle some startup contingencies
This commit is contained in:
parent
893e7fbb1b
commit
db9a2152fa
16 changed files with 467 additions and 30 deletions
|
@ -916,6 +916,17 @@
|
||||||
"Loading 3D model.": "Loading 3D model.",
|
"Loading 3D model.": "Loading 3D model.",
|
||||||
"Click here": "Click here",
|
"Click here": "Click here",
|
||||||
"PDF opened externally. %click_here% to open it again.": "PDF opened externally. %click_here% to open it again.",
|
"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",
|
"%numberOfMonthsSincePublish% years ago": "%numberOfMonthsSincePublish% years ago",
|
||||||
"%numberOfYearsSincePublish% years ago": "%numberOfYearsSincePublish% years ago",
|
"%numberOfYearsSincePublish% years ago": "%numberOfYearsSincePublish% years ago",
|
||||||
"%numberOfYearsSincePublish% year ago": "%numberOfYearsSincePublish% year ago",
|
"%numberOfYearsSincePublish% year ago": "%numberOfYearsSincePublish% year ago",
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
|
||||||
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
|
||||||
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
||||||
import { doSetLanguage } from 'redux/actions/settings';
|
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';
|
import App from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -30,6 +30,7 @@ const perform = dispatch => ({
|
||||||
signIn: () => dispatch(doSignIn()),
|
signIn: () => dispatch(doSignIn()),
|
||||||
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
||||||
checkSync: () => dispatch(doSyncWithPreferences()),
|
checkSync: () => dispatch(doSyncWithPreferences()),
|
||||||
|
updatePreferences: () => dispatch(doGetAndPopulatePreferences()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default hot(
|
export default hot(
|
||||||
|
|
|
@ -46,6 +46,7 @@ type Props = {
|
||||||
isUpgradeAvailable: boolean,
|
isUpgradeAvailable: boolean,
|
||||||
autoUpdateDownloaded: boolean,
|
autoUpdateDownloaded: boolean,
|
||||||
checkSync: () => void,
|
checkSync: () => void,
|
||||||
|
updatePreferences: () => void,
|
||||||
syncEnabled: boolean,
|
syncEnabled: boolean,
|
||||||
uploadCount: number,
|
uploadCount: number,
|
||||||
balance: ?number,
|
balance: ?number,
|
||||||
|
@ -73,6 +74,7 @@ function App(props: Props) {
|
||||||
language,
|
language,
|
||||||
languages,
|
languages,
|
||||||
setLanguage,
|
setLanguage,
|
||||||
|
updatePreferences,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const appRef = useRef();
|
const appRef = useRef();
|
||||||
|
@ -168,6 +170,12 @@ function App(props: Props) {
|
||||||
}
|
}
|
||||||
}, [hasVerifiedEmail, syncEnabled, checkSync]);
|
}, [hasVerifiedEmail, syncEnabled, checkSync]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasVerifiedEmail === false) {
|
||||||
|
updatePreferences();
|
||||||
|
}
|
||||||
|
}, [hasVerifiedEmail]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (syncError) {
|
if (syncError) {
|
||||||
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
|
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
|
||||||
|
|
24
ui/component/settingWalletServer/index.js
Normal file
24
ui/component/settingWalletServer/index.js
Normal file
|
@ -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);
|
35
ui/component/settingWalletServer/internal/displayRow.jsx
Normal file
35
ui/component/settingWalletServer/internal/displayRow.jsx
Normal file
|
@ -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 (
|
||||||
|
<tr>
|
||||||
|
<td className="table__item--actionable">
|
||||||
|
{host}
|
||||||
|
</td>
|
||||||
|
<td className="table__item--actionable">
|
||||||
|
{port}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{available && <Icon icon={ICONS.SUBSCRIBE} />}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Button button={'link'} icon={ICONS.REMOVE} onClick={() => remove(index)} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServerDisplayRow;
|
47
ui/component/settingWalletServer/internal/inputRow.jsx
Normal file
47
ui/component/settingWalletServer/internal/inputRow.jsx
Normal file
|
@ -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 (
|
||||||
|
<tr>
|
||||||
|
<td> {/* host */}
|
||||||
|
<FormField type="text" value={hostString} onChange={e => setHostString(e.target.value)}/>
|
||||||
|
</td>
|
||||||
|
<td> {/* port */}
|
||||||
|
<FormField type="text" value={portString} onChange={e => setPortString(e.target.value)}/>
|
||||||
|
</td>
|
||||||
|
<td />
|
||||||
|
<td>
|
||||||
|
<Button button={'link'} icon={ICONS.ADD} disabled={!valid} onClick={onClick}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServerInputRow;
|
174
ui/component/settingWalletServer/view.jsx
Normal file
174
ui/component/settingWalletServer/view.jsx
Normal file
|
@ -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<string> // ['host', 'port']
|
||||||
|
|
||||||
|
type DisplayOfServer = {
|
||||||
|
host: string,
|
||||||
|
port: string,
|
||||||
|
availability: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerStatus = Array<StatusOfServer>
|
||||||
|
type ServerConfig = Array<ServerInConfig>
|
||||||
|
type DisplayList = Array<DisplayOfServer>
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<React.Fragment>
|
||||||
|
<FormField
|
||||||
|
type="radio"
|
||||||
|
name="default_wallet_servers"
|
||||||
|
checked={!custom}
|
||||||
|
label={__('lbry.tv wallet servers')}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
doClear();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
type="radio"
|
||||||
|
name="custom_wallet_servers"
|
||||||
|
checked={custom}
|
||||||
|
onChange={e => {
|
||||||
|
setCustom(e.target.checked);
|
||||||
|
if (e.target.checked) {
|
||||||
|
setWalletServers(makeServerParam(customServers));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
label={__('Custom wallet servers')}
|
||||||
|
/>
|
||||||
|
{custom && (
|
||||||
|
<div>
|
||||||
|
<table className="table table--transactions">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{__('Host')}</th>
|
||||||
|
<th>{__('Port')} </th>
|
||||||
|
<th>{__('Available')} </th>
|
||||||
|
<th>{__('Add/Delete')} </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{servers &&
|
||||||
|
makeDisplayList(servers).map((t, i) => (
|
||||||
|
<ServerDisplayRow key={`${t.host}:${t.port}`} host={t.host} port={t.port} available={t.available} index={i} remove={onDelete} />
|
||||||
|
))}
|
||||||
|
<ServerInputRow update={onAdd} />
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="help">{__(`Choose a different provider's wallet server.`)}</p>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingWalletServer;
|
|
@ -2,8 +2,9 @@ import * as MODALS from 'constants/modal_types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectDaemonVersionMatched, selectModal } from 'redux/selectors/app';
|
import { selectDaemonVersionMatched, selectModal } from 'redux/selectors/app';
|
||||||
import { doCheckDaemonVersion, doOpenModal, doHideModal } from 'redux/actions/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 * as settings from 'constants/settings';
|
||||||
|
import { doToast, DAEMON_SETTINGS } from 'lbry-redux';
|
||||||
import SplashScreen from './view';
|
import SplashScreen from './view';
|
||||||
|
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
|
@ -20,6 +21,8 @@ const perform = dispatch => ({
|
||||||
dispatch(doOpenModal(MODALS.WALLET_UNLOCK, { shouldTryWithBlankPassword })),
|
dispatch(doOpenModal(MODALS.WALLET_UNLOCK, { shouldTryWithBlankPassword })),
|
||||||
hideModal: () => dispatch(doHideModal()),
|
hideModal: () => dispatch(doHideModal()),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
|
clearWalletServers: () => dispatch(doClearDaemonSetting(DAEMON_SETTINGS.LBRYUM_SERVERS)),
|
||||||
|
doShowSnackBar: message => dispatch(doToast({ isError: true, message })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -13,6 +13,8 @@ import I18nMessage from 'component/i18nMessage';
|
||||||
import 'css-doodle';
|
import 'css-doodle';
|
||||||
|
|
||||||
const FORTY_FIVE_SECONDS = 45 * 1000;
|
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 = {
|
type Props = {
|
||||||
checkDaemonVersion: () => Promise<any>,
|
checkDaemonVersion: () => Promise<any>,
|
||||||
|
@ -26,6 +28,8 @@ type Props = {
|
||||||
},
|
},
|
||||||
animationHidden: boolean,
|
animationHidden: boolean,
|
||||||
setClientSetting: (string, boolean) => void,
|
setClientSetting: (string, boolean) => void,
|
||||||
|
clearWalletServers: () => void,
|
||||||
|
doShowSnackBar: string => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -35,6 +39,10 @@ type State = {
|
||||||
error: boolean,
|
error: boolean,
|
||||||
isRunning: boolean,
|
isRunning: boolean,
|
||||||
launchWithIncompatibleDaemon: boolean,
|
launchWithIncompatibleDaemon: boolean,
|
||||||
|
walletServerAvailable: boolean,
|
||||||
|
waitingForWallet: number,
|
||||||
|
hasPopulated: boolean,
|
||||||
|
hasReconnected: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class SplashScreen extends React.PureComponent<Props, State> {
|
export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
|
@ -48,6 +56,7 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
error: false,
|
error: false,
|
||||||
launchWithIncompatibleDaemon: false,
|
launchWithIncompatibleDaemon: false,
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
|
waitingForWallet: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
(this: any).renderModals = this.renderModals.bind(this);
|
(this: any).renderModals = this.renderModals.bind(this);
|
||||||
|
@ -98,7 +107,7 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus() {
|
updateStatus() {
|
||||||
const { modal, notifyUnlockWallet } = this.props;
|
const { modal, notifyUnlockWallet, clearWalletServers, doShowSnackBar } = this.props;
|
||||||
const { launchedModal } = this.state;
|
const { launchedModal } = this.state;
|
||||||
|
|
||||||
Lbry.status().then(status => {
|
Lbry.status().then(status => {
|
||||||
|
@ -118,6 +127,11 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
this.updateStatusCallback(status);
|
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 {
|
} else {
|
||||||
this.updateStatusCallback(status);
|
this.updateStatusCallback(status);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +160,9 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
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({
|
this.setState({
|
||||||
message: __('Blockchain Sync'),
|
message: __('Blockchain Sync'),
|
||||||
details: `${__('Catching up...')} (${wallet.headers_synchronization_progress}%)`,
|
details: `${__('Catching up...')} (${wallet.headers_synchronization_progress}%)`,
|
||||||
|
@ -160,7 +176,7 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.updateStatus();
|
this.updateStatus();
|
||||||
}, 500);
|
}, UPDATE_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
runWithIncompatibleDaemon() {
|
runWithIncompatibleDaemon() {
|
||||||
|
|
|
@ -114,6 +114,7 @@ export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
||||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||||
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
|
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
|
||||||
export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
|
export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
|
||||||
|
export const WALLET_SERVERS_CACHED = 'WALLET_SERVERS_CACHED';
|
||||||
|
|
||||||
// User
|
// User
|
||||||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Button from 'component/button';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import SettingLanguage from 'component/settingLanguage';
|
import SettingLanguage from 'component/settingLanguage';
|
||||||
|
import SettingWalletServer from 'component/settingWalletServer';
|
||||||
import SettingAutoLaunch from 'component/settingAutoLaunch';
|
import SettingAutoLaunch from 'component/settingAutoLaunch';
|
||||||
import FileSelector from 'component/common/file-selector';
|
import FileSelector from 'component/common/file-selector';
|
||||||
import SyncToggle from 'component/syncToggle';
|
import SyncToggle from 'component/syncToggle';
|
||||||
|
@ -225,6 +226,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
<div>
|
<div>
|
||||||
<Card title={__('Language')} actions={<SettingLanguage />} />
|
<Card title={__('Language')} actions={<SettingLanguage />} />
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
|
<Card title={__('Wallet Server')} actions={<SettingWalletServer />} />
|
||||||
<Card
|
<Card
|
||||||
title={__('Sync')}
|
title={__('Sync')}
|
||||||
subtitle={
|
subtitle={
|
||||||
|
|
|
@ -449,29 +449,34 @@ export function doSignOut() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doGetAndPopulatePreferences() {
|
||||||
|
return dispatch => {
|
||||||
|
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() {
|
export function doSyncWithPreferences() {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
function handleSyncComplete() {
|
function handleSyncComplete() {
|
||||||
|
// we just got sync data, better update our channels
|
||||||
dispatch(doFetchChannelListMine());
|
dispatch(doFetchChannelListMine());
|
||||||
|
dispatch(doGetAndPopulatePreferences());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSavedPassword().then(password => {
|
return getSavedPassword().then(password => {
|
||||||
|
|
|
@ -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 SETTINGS from 'constants/settings';
|
||||||
import * as LOCAL_ACTIONS from 'constants/action_types';
|
import * as LOCAL_ACTIONS from 'constants/action_types';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
|
@ -23,13 +23,22 @@ export function doFetchDaemonSettings() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doSetDaemonSetting(key, value) {
|
export function doClearDaemonSetting(key) {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
const newSettings = {
|
const clearKey = {
|
||||||
key,
|
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 => {
|
Lbry.settings_get().then(settings => {
|
||||||
analytics.toggle(settings.share_usage_data);
|
analytics.toggle(settings.share_usage_data);
|
||||||
dispatch({
|
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) {
|
export function doSetClientSetting(key, value) {
|
||||||
return {
|
return {
|
||||||
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
||||||
|
|
|
@ -2,11 +2,16 @@ import * as ACTIONS from 'constants/action_types';
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
||||||
|
import { ACTIONS as LBRY_REDUX_ACTIONS, SHARED_PREFS } from 'lbry-redux';
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
isNight: false,
|
isNight: false,
|
||||||
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
|
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
|
||||||
|
customWalletServers: [],
|
||||||
|
sharedPrefs: {
|
||||||
|
[SHARED_PREFS.WALLET_SERVERS]: false,
|
||||||
|
},
|
||||||
daemonSettings: {},
|
daemonSettings: {},
|
||||||
clientSettings: {
|
clientSettings: {
|
||||||
// UX
|
// 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) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) return handler(state, action);
|
||||||
|
|
|
@ -34,6 +34,22 @@ export const selectIsNight = createSelector(
|
||||||
state => state.isNight
|
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(
|
export const selectThemePath = createSelector(
|
||||||
selectTheme,
|
selectTheme,
|
||||||
selectAutomaticDarkModeEnabled,
|
selectAutomaticDarkModeEnabled,
|
||||||
|
|
|
@ -108,6 +108,7 @@ const triggerSharedStateActions = [
|
||||||
LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW,
|
LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW,
|
||||||
LBRY_REDUX_ACTIONS.TOGGLE_BLOCK_CHANNEL,
|
LBRY_REDUX_ACTIONS.TOGGLE_BLOCK_CHANNEL,
|
||||||
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
|
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
|
||||||
|
LBRY_REDUX_ACTIONS.SHARED_PREFERENCE_SET,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,6 +130,7 @@ const sharedStateFilters = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
blocked: { source: 'blocked', property: 'blockedChannels' },
|
blocked: { source: 'blocked', property: 'blockedChannels' },
|
||||||
|
settings: { source: 'settings', property: 'sharedPrefs'},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sharedStateCb = ({ dispatch, getState }) => {
|
const sharedStateCb = ({ dispatch, getState }) => {
|
||||||
|
|
Loading…
Reference in a new issue