sync changes

This commit is contained in:
jessop 2020-09-04 11:02:30 -04:00 committed by Sean Yesmunt
parent bb9bde1c19
commit 6c4842a111
29 changed files with 270 additions and 170 deletions

View file

@ -136,8 +136,8 @@
"imagesloaded": "^4.1.4",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#210bb80f2c49f6a166a8adc602a68b7490d5d23a",
"lbryinc": "lbryio/lbryinc#cff5dd60934c4c6080e135f47ebbece1548c658c",
"lbry-redux": "lbryio/lbry-redux#760623f99c90407ca3f1d06f1cdd2506fb2e443a",
"lbryinc": "lbryio/lbryinc#35df87d1e69e435e25fc12832b6b1b788f76baaa",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",
"lodash-es": "^4.17.14",

View file

@ -5,13 +5,18 @@ import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user';
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
import { doFetchChannelListMine, SETTINGS } from 'lbry-redux';
import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings';
import {
makeSelectClientSetting,
selectLoadedLanguages,
selectSyncSigninPref,
selectThemePath,
} from 'redux/selectors/settings';
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
import { doSetLanguage } from 'redux/actions/settings';
import { doSetLanguage, doUpdateSyncPref } from 'redux/actions/settings';
import { doSyncSubscribe } from 'redux/actions/syncwrapper';
import {
doDownloadUpgradeRequested,
doSignIn,
doSyncWithPreferences,
doGetAndPopulatePreferences,
doAnalyticsTagSync,
} from 'redux/actions/app';
@ -22,14 +27,15 @@ const select = state => ({
accessToken: selectAccessToken(state),
theme: selectThemePath(state),
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
languages: selectLoadedLanguages(state),
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
isUpgradeAvailable: selectIsUpgradeAvailable(state),
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
syncError: selectGetSyncErrorMessage(state),
uploadCount: selectUploadCount(state),
rewards: selectUnclaimedRewards(state),
isAuthenticated: selectUserVerifiedEmail(state),
signInSyncPref: selectSyncSigninPref(state),
});
const perform = dispatch => ({
@ -39,8 +45,9 @@ const perform = dispatch => ({
setLanguage: language => dispatch(doSetLanguage(language)),
signIn: () => dispatch(doSignIn()),
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
checkSync: () => dispatch(doSyncWithPreferences()),
updatePreferences: () => dispatch(doGetAndPopulatePreferences()),
updateSyncPref: () => dispatch(doUpdateSyncPref()),
syncSubscribe: () => dispatch(doSyncSubscribe()),
setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)),
});

View file

@ -38,7 +38,6 @@ export const MAIN_WRAPPER_CLASS = 'main-wrapper';
// @if TARGET='app'
export const IS_MAC = process.platform === 'darwin';
// @endif
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
// button numbers pulled from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
const MOUSE_BACK_BTN = 3;
@ -67,17 +66,20 @@ type Props = {
setLanguage: string => void,
isUpgradeAvailable: boolean,
autoUpdateDownloaded: boolean,
checkSync: () => void,
updatePreferences: () => void,
syncEnabled: boolean,
updatePreferences: () => Promise<any>,
updateSyncPref: () => void,
uploadCount: number,
balance: ?number,
syncError: ?string,
syncEnabled: boolean,
rewards: Array<Reward>,
setReferrer: (string, boolean) => void,
analyticsTagSync: () => void,
isAuthenticated: boolean,
socketConnect: () => void,
syncSubscribe: () => void,
syncEnabled: boolean,
signInSyncPref: boolean,
};
function App(props: Props) {
@ -90,8 +92,6 @@ function App(props: Props) {
autoUpdateDownloaded,
isUpgradeAvailable,
requestDownloadUpgrade,
syncEnabled,
checkSync,
uploadCount,
history,
syncError,
@ -99,15 +99,19 @@ function App(props: Props) {
languages,
setLanguage,
updatePreferences,
updateSyncPref,
rewards,
setReferrer,
analyticsTagSync,
isAuthenticated,
syncSubscribe,
signInSyncPref,
} = props;
const appRef = useRef();
const isEnhancedLayout = useKonamiListener();
const [hasSignedIn, setHasSignedIn] = useState(false);
const [readyForSync, setReadyForSync] = useState(false);
const [readyForPrefs, setReadyForPrefs] = useState(false);
const hasVerifiedEmail = user && user.has_verified_email;
const isRewardApproved = user && user.is_reward_approved;
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
@ -229,35 +233,40 @@ function App(props: Props) {
}
}, [previousRewardApproved, isRewardApproved]);
// Keep this at the end to ensure initial setup effects are run first
useEffect(() => {
if (!hasSignedIn && hasVerifiedEmail) {
signIn();
setHasSignedIn(true);
}
}, [hasVerifiedEmail, signIn, hasSignedIn]);
// @if TARGET='app'
useEffect(() => {
updatePreferences();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (updatePreferences && readyForPrefs) {
updatePreferences().then(() => {
setReadyForSync(true);
});
}
}, [updatePreferences, setReadyForSync, readyForPrefs, hasVerifiedEmail]);
// @endif
// ready for sync syncs, however after signin when hasVerifiedEmail, that syncs too.
useEffect(() => {
if (hasVerifiedEmail && syncEnabled) {
checkSync();
analyticsTagSync();
let syncInterval = setInterval(() => {
checkSync();
}, SYNC_INTERVAL);
return () => {
clearInterval(syncInterval);
};
if (readyForSync && hasVerifiedEmail) {
// Copy sync checkbox to settings and push to preferences
// before sync if false, after sync if true so as not to change timestamp.
if (signInSyncPref === false) {
updateSyncPref();
}
syncSubscribe();
if (signInSyncPref === true) {
updateSyncPref();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasVerifiedEmail, syncEnabled, checkSync]);
}, [readyForSync, hasVerifiedEmail, signInSyncPref, updateSyncPref, syncSubscribe]);
// We know someone is logging in or not when we get their user object {}
// We'll use this to determine when it's time to pull preferences
// This will no longer work if desktop users no longer get a user object from lbryinc
useEffect(() => {
if (user) {
setReadyForPrefs(true);
}
}, [user, setReadyForPrefs]);
useEffect(() => {
if (syncError && isAuthenticated) {
@ -266,6 +275,15 @@ function App(props: Props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [syncError, pathname, isAuthenticated]);
// Keep this at the end to ensure initial setup effects are run first
useEffect(() => {
if (!hasSignedIn && hasVerifiedEmail) {
signIn();
setHasSignedIn(true);
if (IS_WEB) setReadyForSync(true);
}
}, [hasVerifiedEmail, signIn, hasSignedIn]);
// @if TARGET='web'
useDegradedPerformance(setLbryTvApiStatus);
// @endif

View file

@ -4,7 +4,7 @@ import { selectBalance, formatCredits, SETTINGS } from 'lbry-redux';
import { selectGetSyncErrorMessage } from 'lbryinc';
import { selectUserVerifiedEmail, selectUserEmail, selectEmailToVerify } from 'redux/selectors/user';
import { doClearEmailEntry, doClearPasswordEntry } from 'redux/actions/user';
import { doSetClientSetting, doSyncClientSettings } from 'redux/actions/settings';
import { doSetClientSetting } from 'redux/actions/settings';
import { doSignOut, doOpenModal } from 'redux/actions/app';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import Header from './view';
@ -25,8 +25,7 @@ const select = state => ({
});
const perform = dispatch => ({
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
syncSettings: () => dispatch(doSyncClientSettings()),
setClientSetting: (key, value, push) => dispatch(doSetClientSetting(key, value, push)),
signOut: () => dispatch(doSignOut()),
openChannelCreate: () => dispatch(doOpenModal(MODALS.CREATE_CHANNEL)),
openSignOutModal: () => dispatch(doOpenModal(MODALS.SIGN_OUT)),

View file

@ -37,7 +37,7 @@ type Props = {
},
currentTheme: string,
automaticDarkModeEnabled: boolean,
setClientSetting: (string, boolean | string) => void,
setClientSetting: (string, boolean | string, ?boolean) => void,
hideBalance: boolean,
email: ?string,
authenticated: boolean,
@ -56,7 +56,6 @@ type Props = {
clearEmailEntry: () => void,
clearPasswordEntry: () => void,
hasNavigated: boolean,
syncSettings: () => void,
sidebarOpen: boolean,
setSidebarOpen: boolean => void,
isAbsoluteSideNavHidden: boolean,
@ -80,7 +79,6 @@ const Header = (props: Props) => {
clearPasswordEntry,
emailToVerify,
backout,
syncSettings,
sidebarOpen,
setSidebarOpen,
isAbsoluteSideNavHidden,
@ -149,11 +147,10 @@ const Header = (props: Props) => {
}
if (currentTheme === 'dark') {
setClientSetting(SETTINGS.THEME, 'light');
setClientSetting(SETTINGS.THEME, 'light', true);
} else {
setClientSetting(SETTINGS.THEME, 'dark');
setClientSetting(SETTINGS.THEME, 'dark', true);
}
syncSettings();
}
function getWalletTitle() {

View file

@ -1,8 +1,8 @@
import { connect } from 'react-redux';
import { selectPublishFormValues, doUpdatePublishForm } from 'lbry-redux';
import PublishPage from './view';
import { selectUser, selectAccessToken } from '../../redux/selectors/user';
import {doFetchAccessToken} from '../../redux/actions/user';
import { selectUser, selectAccessToken } from 'redux/selectors/user';
import { doFetchAccessToken } from 'redux/actions/user';
const select = state => ({
...selectPublishFormValues(state),

View file

@ -3,8 +3,7 @@ import { selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectHasNavigated, selectScrollStartingPosition, selectWelcomeVersion } from 'redux/selectors/app';
import Router from './view';
import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux';
import { doSetHasNavigated, doSyncWithPreferences } from 'redux/actions/app';
import { doSyncClientSettings } from 'redux/actions/settings';
import { doSetHasNavigated } from 'redux/actions/app';
const select = state => {
const { pathname, hash } = state.router.location;
const urlPath = pathname + hash;
@ -34,8 +33,6 @@ const select = state => {
const perform = dispatch => ({
setHasNavigated: () => dispatch(doSetHasNavigated()),
syncSettings: () => dispatch(doSyncClientSettings()),
checkSync: () => dispatch(doSyncWithPreferences()),
});
export default connect(select, perform)(Router);

View file

@ -97,8 +97,6 @@ type Props = {
welcomeVersion: number,
hasNavigated: boolean,
setHasNavigated: () => void,
syncSettings: () => void,
checkSync: () => void,
};
function AppRouter(props: Props) {
@ -112,14 +110,11 @@ function AppRouter(props: Props) {
welcomeVersion,
hasNavigated,
setHasNavigated,
syncSettings,
checkSync,
} = props;
const { entries } = history;
const entryIndex = history.index;
const urlParams = new URLSearchParams(search);
const resetScroll = urlParams.get('reset_scroll');
const [prevPath, setPrevPath] = React.useState(pathname);
// For people arriving at settings page from deeplinks, know whether they can "go back"
useEffect(() => {
@ -131,27 +126,6 @@ function AppRouter(props: Props) {
return unlisten;
}, [hasNavigated, setHasNavigated]);
// Sync when no longer on a settings page, or when entering settings pages
useEffect(() => {
const unlisten = history.listen(location => {
if (!location.pathname.includes(PAGES.SETTINGS) && prevPath.includes(PAGES.SETTINGS)) {
syncSettings();
} else if (location.pathname.includes(PAGES.SETTINGS) && !prevPath.includes(PAGES.SETTINGS)) {
checkSync();
}
});
return unlisten;
}, [prevPath, syncSettings, checkSync]);
useEffect(() => {
const unlisten = history.listen(location => {
if (prevPath !== location.pathname && setPrevPath) {
setPrevPath(location.pathname);
}
});
return unlisten;
}, [prevPath, setPrevPath]);
useEffect(() => {
if (uri) {
const { channelName, streamName } = parseURI(uri);

View file

@ -1,5 +1,6 @@
import { connect } from 'react-redux';
import { doGetSync, selectGetSyncIsPending, selectSyncApplyPasswordError } from 'lbryinc';
import { selectGetSyncIsPending, selectSyncApplyPasswordError } from 'lbryinc';
import { doGetSyncDesktop } from 'redux/actions/syncwrapper';
import { selectUserEmail } from 'redux/selectors/user';
import { doSetClientSetting } from 'redux/actions/settings';
import { doSignOut, doHandleSyncComplete } from 'redux/actions/app';
@ -12,7 +13,7 @@ const select = state => ({
});
const perform = dispatch => ({
getSync: (password, cb) => dispatch(doGetSync(password, cb)),
getSync: (cb, password) => dispatch(doGetSyncDesktop(cb, password)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
handleSyncComplete: (error, hasDataChanged) => dispatch(doHandleSyncComplete(error, hasDataChanged)),
signOut: () => dispatch(doSignOut()),

View file

@ -8,7 +8,7 @@ import usePersistedState from 'effects/use-persisted-state';
import I18nMessage from 'component/i18nMessage';
type Props = {
getSync: (?string, (any, boolean) => void) => void,
getSync: ((any, boolean) => void, ?string) => void,
getSyncIsPending: boolean,
email: string,
passwordError: boolean,
@ -22,13 +22,13 @@ function SyncPassword(props: Props) {
const [rememberPassword, setRememberPassword] = usePersistedState(true);
function handleSubmit() {
getSync(password, (error, hasDataChanged) => {
getSync((error, hasDataChanged) => {
handleSyncComplete(error, hasDataChanged);
if (!error) {
setSavedPassword(password, rememberPassword);
}
});
}, password);
}
return (

View file

@ -7,7 +7,7 @@ import {
selectUser,
} from 'redux/selectors/user';
import { DAEMON_SETTINGS, SETTINGS } from 'lbry-redux';
import { doSetClientSetting, doSetDaemonSetting } from 'redux/actions/settings';
import { doSetSyncPref, doSetDaemonSetting } from 'redux/actions/settings';
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
import UserEmailNew from './view';
@ -21,7 +21,7 @@ const select = state => ({
});
const perform = dispatch => ({
setSync: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)),
setSync: value => dispatch(doSetSyncPref(value)),
setShareDiagnosticData: shouldShareData =>
dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, shouldShareData)),
doSignUp: (email, password) => dispatch(doUserSignUp(email, password)),

View file

@ -8,7 +8,7 @@ import {
selectEmailNewIsPending,
} from 'redux/selectors/user';
import { doUserCheckIfEmailExists, doClearEmailEntry } from 'redux/actions/user';
import { doSetClientSetting } from 'redux/actions/settings';
import { doSetSyncPref } from 'redux/actions/settings';
import UserEmailReturning from './view';
const select = state => ({
@ -23,5 +23,5 @@ const select = state => ({
export default connect(select, {
doUserCheckIfEmailExists,
doClearEmailEntry,
doSetClientSetting,
doSetSyncPref,
})(UserEmailReturning);

View file

@ -1,5 +1,4 @@
// @flow
import { SETTINGS } from 'lbry-redux';
import * as PAGES from 'constants/pages';
import React, { useState } from 'react';
import { FormField, Form } from 'component/common/form';
@ -18,7 +17,8 @@ type Props = {
doClearEmailEntry: () => void,
doUserSignIn: (string, ?string) => void,
doUserCheckIfEmailExists: string => void,
doSetClientSetting: (string, boolean) => void,
doSetSyncPref: boolean => void,
doSetClientSetting: (string, boolean, ?boolean) => void,
isPending: boolean,
};
@ -30,7 +30,7 @@ function UserEmailReturning(props: Props) {
emailToVerify,
doClearEmailEntry,
emailDoesNotExist,
doSetClientSetting,
doSetSyncPref,
isPending,
} = props;
const { push, location } = useHistory();
@ -48,7 +48,7 @@ function UserEmailReturning(props: Props) {
function handleSubmit() {
// @if TARGET='app'
doSetClientSetting(SETTINGS.ENABLE_SYNC, syncEnabled);
doSetSyncPref(syncEnabled);
// @endif
doUserCheckIfEmailExists(email);
}

View file

@ -7,7 +7,7 @@ import Spinner from 'component/spinner';
type Props = {
user: ?User,
history: { push: string => void },
history: { push: string => void, replace: string => void },
location: { search: string },
userFetchPending: boolean,
doUserSignIn: string => void,
@ -28,7 +28,7 @@ function UserSignIn(props: Props) {
React.useEffect(() => {
if (hasVerifiedEmail || (!showEmail && !showPassword && !showLoading)) {
history.push(redirect || '/');
history.replace(redirect || '/');
}
}, [showEmail, showPassword, showLoading, hasVerifiedEmail]);

View file

@ -27,6 +27,7 @@ export const PASSWORD_SAVED = 'PASSWORD_SAVED';
export const SET_WELCOME_VERSION = 'SET_WELCOME_VERSION';
export const SET_ALLOW_ANALYTICS = 'SET_ALLOW_ANALYTICS';
export const SET_HAS_NAVIGATED = 'SET_HAS_NAVIGATED';
export const SET_SYNC_LOCK = 'SET_SYNC_LOCK';
// Navigation
export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
@ -132,6 +133,7 @@ export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
export const FINDING_FFMPEG_STARTED = 'FINDING_FFMPEG_STARTED';
export const FINDING_FFMPEG_COMPLETED = 'FINDING_FFMPEG_COMPLETED';
export const SYNC_CLIENT_SETTINGS = 'SYNC_CLIENT_SETTINGS';
export const SYNC_PREFERENCE_CHANGED = 'SYNC_PREFERENCE_CHANGED';
// User
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';

View file

@ -117,30 +117,10 @@ doAuthTokenRefresh();
// We keep a local variable for authToken because `ipcRenderer.send` does not
// contain a response, so there is no way to know when it's been set
let authToken;
Lbryio.setOverride(
'setAuthToken',
status =>
new Promise(resolve => {
Lbryio.call(
'user',
'new',
{
auth_token: '',
language: DEFAULT_LANGUAGE,
app_id: status.installation_id,
},
'post'
).then(response => {
if (!response.auth_token) {
throw new Error(__('auth_token is missing from response'));
}
authToken = response.auth_token;
setAuthToken(authToken);
resolve(authToken);
});
})
);
Lbryio.setOverride('setAuthToken', authToken => {
setAuthToken(authToken);
return authToken;
});
Lbryio.setOverride(
'getAuthToken',

View file

@ -6,7 +6,8 @@ import {
doClearDaemonSetting,
doSetClientSetting,
doSetDarkTime,
doSyncClientSettings,
doEnterSettingsPage,
doExitSettingsPage,
} from 'redux/actions/settings';
import { doSetPlayingUri } from 'redux/actions/content';
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
@ -37,7 +38,6 @@ const select = state => ({
const perform = dispatch => ({
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
syncSettings: () => dispatch(doSyncClientSettings()),
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
toggle3PAnalytics: allow => dispatch(doToggle3PAnalytics(allow)),
clearCache: () => dispatch(doClearCache()),
@ -47,6 +47,8 @@ const perform = dispatch => ({
clearPlayingUri: () => dispatch(doSetPlayingUri(null)),
setDarkTime: (time, options) => dispatch(doSetDarkTime(time, options)),
openModal: (id, params) => dispatch(doOpenModal(id, params)),
enterSettings: () => dispatch(doEnterSettingsPage()),
exitSettings: () => dispatch(doExitSettingsPage()),
});
export default connect(select, perform)(SettingsPage);

View file

@ -4,7 +4,6 @@ import * as MODALS from 'constants/modal_types';
import * as ICONS from 'constants/icons';
import * as React from 'react';
import { SETTINGS } from 'lbry-redux';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import Page from 'component/page';
@ -66,6 +65,8 @@ type Props = {
openModal: string => void,
language?: string,
syncEnabled: boolean,
enterSettings: () => void,
exitSettings: () => void,
};
type State = {
@ -89,7 +90,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
}
componentDidMount() {
const { isAuthenticated } = this.props;
const { isAuthenticated, enterSettings } = this.props;
if (isAuthenticated || !IS_WEB) {
this.props.updateWalletStatus();
@ -99,6 +100,12 @@ class SettingsPage extends React.PureComponent<Props, State> {
}
});
}
enterSettings();
}
componentWillUnmount() {
const { exitSettings } = this.props;
exitSettings();
}
onThemeChange(event: SyntheticInputEvent<*>) {
@ -211,11 +218,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
<Card
title={__('Sync')}
subtitle={
walletEncrypted && !storedPassword
? __("To enable this feature, check 'Save Password' the next time you start the app.")
walletEncrypted && !storedPassword && storedPassword !== ''
? __("To enable Sync, close LBRY completely and check 'Remember Password' during wallet unlock.")
: null
}
actions={<SyncToggle disabled={walletEncrypted && !storedPassword} />}
actions={<SyncToggle disabled={walletEncrypted && !storedPassword && storedPassword !== ''} />}
/>
{/* @endif */}

View file

@ -6,7 +6,8 @@ import {
doClearDaemonSetting,
doSetClientSetting,
doFindFFmpeg,
doSyncClientSettings,
doEnterSettingsPage,
doExitSettingsPage,
} from 'redux/actions/settings';
import {
makeSelectClientSetting,
@ -34,7 +35,6 @@ const select = state => ({
const perform = dispatch => ({
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
syncSettings: () => dispatch(doSyncClientSettings()),
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
clearCache: () => dispatch(doClearCache()),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
@ -43,6 +43,8 @@ const perform = dispatch => ({
updateWalletStatus: () => dispatch(doWalletStatus()),
confirmForgetPassword: modalProps => dispatch(doNotifyForgetPassword(modalProps)),
findFFmpeg: () => dispatch(doFindFFmpeg()),
enterSettings: () => dispatch(doEnterSettingsPage()),
exitSettings: () => dispatch(doExitSettingsPage()),
});
export default connect(select, perform)(SettingsPage);

View file

@ -55,6 +55,8 @@ type Props = {
language?: string,
syncEnabled: boolean,
syncSettings: () => void,
enterSettings: () => void,
exitSettings: () => void,
};
type State = {
@ -81,7 +83,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
}
componentDidMount() {
const { isAuthenticated, ffmpegStatus, daemonSettings, findFFmpeg } = this.props;
const { isAuthenticated, ffmpegStatus, daemonSettings, findFFmpeg, enterSettings } = this.props;
// @if TARGET='app'
const { available } = ffmpegStatus;
@ -102,6 +104,12 @@ class SettingsPage extends React.PureComponent<Props, State> {
}
});
}
enterSettings();
}
componentWillUnmount() {
const { exitSettings } = this.props;
exitSettings();
}
onFFmpegFolder(path: string) {

View file

@ -46,11 +46,11 @@ import {
import { selectDaemonSettings } from 'redux/selectors/settings';
import { selectUser } from 'redux/selectors/user';
// import { selectDaemonSettings } from 'redux/selectors/settings';
import { doGetSync } from 'lbryinc';
import { doSyncSubscribe } from 'redux/actions/syncwrapper';
import { doAuthenticate } from 'redux/actions/user';
import { lbrySettings as config, version as appVersion } from 'package.json';
import analytics, { SHARE_INTERNAL } from 'analytics';
import { doSignOutCleanup, deleteSavedPassword, getSavedPassword } from 'util/saved-passwords';
import { doSignOutCleanup, deleteSavedPassword } from 'util/saved-passwords';
import { doSocketConnect } from 'redux/actions/websocket';
import { stringifyServerParam, shouldSetSetting } from 'util/sync-settings';
import sha256 from 'crypto-js/sha256';
@ -89,6 +89,13 @@ export function doUpdateDownloadProgress(percent) {
};
}
export function doSetSyncLock(lock) {
return {
type: ACTIONS.SET_SYNC_LOCK,
data: lock,
};
}
export function doSkipUpgrade() {
return {
type: ACTIONS.SKIP_UPGRADE,
@ -622,6 +629,7 @@ export function doGetAndPopulatePreferences() {
}
// @endif
}
return true;
}
function failCb() {
@ -633,9 +641,10 @@ export function doGetAndPopulatePreferences() {
})
);
});
return false;
}
doPreferenceGet(preferenceKey, successCb, failCb);
return doPreferenceGet(preferenceKey, successCb, failCb);
};
}
@ -653,11 +662,5 @@ export function doHandleSyncComplete(error, hasNewData) {
}
export function doSyncWithPreferences() {
return dispatch => {
return getSavedPassword().then(password => {
const passwordArgument = password === null ? '' : password;
dispatch(doGetSync(passwordArgument, (error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
});
};
return dispatch => dispatch(doSyncSubscribe());
}

View file

@ -5,6 +5,9 @@ import analytics from 'analytics';
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
import { launcher } from 'util/autoLaunch';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doGetSyncDesktop, doSyncUnsubscribe } from 'redux/actions/syncwrapper';
import { doGetAndPopulatePreferences, doSetSyncLock } from 'redux/actions/app';
const { DEFAULT_LANGUAGE } = require('config');
const { SDK_SYNC_KEYS } = SHARED_PREFERENCES;
@ -119,7 +122,7 @@ export function doSaveCustomWalletServers(servers) {
};
}
export function doSetClientSetting(key, value) {
export function doSetClientSetting(key, value, pushPrefs) {
return dispatch => {
dispatch({
type: ACTIONS.CLIENT_SETTING_CHANGED,
@ -128,6 +131,30 @@ export function doSetClientSetting(key, value) {
value,
},
});
if (pushPrefs) {
dispatch(doPushSettingsToPrefs());
}
};
}
export function doUpdateSyncPref() {
return (dispatch, getState) => {
const { settings } = getState();
const { syncEnabledPref } = settings || {};
if (syncEnabledPref !== undefined) {
dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, syncEnabledPref, true));
dispatch(doSetSyncPref(undefined));
}
};
}
export function doSetSyncPref(isEnabled) {
return dispatch => {
dispatch({
type: LOCAL_ACTIONS.SYNC_PREFERENCE_CHANGED,
data: isEnabled,
});
};
}
@ -167,15 +194,36 @@ export function doSetDarkTime(value, options) {
};
}
export function doSyncClientSettings() {
return (dispatch, getState) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
if (syncEnabled) {
export function doPushSettingsToPrefs() {
return dispatch => {
return new Promise((resolve, reject) => {
dispatch({
type: LOCAL_ACTIONS.SYNC_CLIENT_SETTINGS,
});
resolve();
});
};
}
export function doEnterSettingsPage() {
return async (dispatch, getState) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const hasVerifiedEmail = state.user && state.user.user && state.user.user.has_verified_email;
dispatch(doSyncUnsubscribe());
if (syncEnabled && hasVerifiedEmail) {
await dispatch(doGetSyncDesktop());
} else {
await dispatch(doGetAndPopulatePreferences());
}
dispatch(doSetSyncLock(true));
};
}
export function doExitSettingsPage() {
return (dispatch, getState) => {
dispatch(doSetSyncLock(false));
dispatch(doPushSettingsToPrefs());
};
}

View file

@ -0,0 +1,57 @@
// @flow
import { doGetSync, selectGetSyncIsPending, selectSetSyncIsPending } from 'lbryinc';
import { SETTINGS } from 'lbry-redux';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { getSavedPassword } from 'util/saved-passwords';
import { doAnalyticsTagSync, doHandleSyncComplete } from 'redux/actions/app';
import { selectSyncIsLocked } from 'redux/selectors/app';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
let syncTimer = null;
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
export const doGetSyncDesktop = (cb?: () => void, password?: string) => (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const getSyncPending = selectGetSyncIsPending(state);
const setSyncPending = selectSetSyncIsPending(state);
const syncLocked = selectSyncIsLocked(state);
return getSavedPassword().then(savedPassword => {
const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword;
if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) {
return dispatch(doGetSync(passwordArgument, cb));
}
});
};
export function doSyncSubscribe() {
return (dispatch: Dispatch, getState: GetState) => {
if (syncTimer) clearInterval(syncTimer);
const state = getState();
const hasVerifiedEmail = selectUserVerifiedEmail(state);
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const syncLocked = selectSyncIsLocked(state);
if (hasVerifiedEmail && syncEnabled && !syncLocked) {
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
dispatch(doAnalyticsTagSync());
syncTimer = setInterval(() => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
if (syncEnabled) {
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
dispatch(doAnalyticsTagSync());
}
}, SYNC_INTERVAL);
}
};
}
export function doSyncUnsubscribe() {
return (dispatch: Dispatch) => {
if (syncTimer) {
clearInterval(syncTimer);
}
};
}

View file

@ -42,6 +42,7 @@ export type AppState = {
welcomeVersion: number,
allowAnalytics: boolean,
hasNavigated: boolean,
syncLocked: boolean,
};
const defaultState: AppState = {
@ -76,6 +77,7 @@ const defaultState: AppState = {
welcomeVersion: 0.0,
allowAnalytics: false,
hasNavigated: false,
syncLocked: false,
};
// @@router comes from react-router
@ -109,6 +111,11 @@ reducers[ACTIONS.DAEMON_READY] = state =>
daemonReady: true,
});
reducers[ACTIONS.SET_SYNC_LOCK] = (state, action) =>
Object.assign({}, state, {
syncLocked: action.data,
});
reducers[ACTIONS.PASSWORD_SAVED] = (state, action) =>
Object.assign({}, state, {
isPasswordSaved: action.data,

View file

@ -24,6 +24,7 @@ const defaultState = {
findingFFmpeg: false,
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
customWalletServers: [],
syncEnabledPref: undefined, // set this during sign in, copy it to clientSettings immediately after prefGet after signedin but before sync
sharedPreferences: {},
daemonSettings: {},
daemonStatus: { ffmpeg_status: {} },
@ -144,17 +145,6 @@ reducers[LBRY_REDUX_ACTIONS.SHARED_PREFERENCE_SET] = (state, action) => {
});
};
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[ACTIONS.SYNC_CLIENT_SETTINGS] = state => {
const { clientSettings } = state;
const sharedPreferences = Object.assign({}, state.sharedPreferences);
@ -163,6 +153,12 @@ reducers[ACTIONS.SYNC_CLIENT_SETTINGS] = state => {
return Object.assign({}, state, { sharedPreferences: newSharedPreferences });
};
reducers[ACTIONS.SYNC_PREFERENCE_CHANGED] = (state, action) => {
return Object.assign({}, state, {
syncEnabledPref: action.data,
});
};
reducers[LBRY_REDUX_ACTIONS.USER_STATE_POPULATE] = (state, action) => {
const { clientSettings: currentClientSettings } = state;
const { settings: sharedPreferences } = action.data;

View file

@ -80,3 +80,5 @@ export const selectAllowAnalytics = createSelector(selectState, state => state.a
export const selectScrollStartingPosition = createSelector(selectState, state => state.currentScroll);
export const selectIsPasswordSaved = createSelector(selectState, state => state.isPasswordSaved);
export const selectSyncIsLocked = createSelector(selectState, state => state.syncLocked);

View file

@ -11,6 +11,8 @@ export const selectFfmpegStatus = createSelector(selectDaemonStatus, status => s
export const selectFindingFFmpeg = createSelector(selectState, state => state.findingFFmpeg || false);
export const selectSyncSigninPref = createSelector(selectState, state => state.syncEnabledPref || undefined);
export const selectClientSettings = createSelector(selectState, state => state.clientSettings || {});
export const selectLoadedLanguages = createSelector(selectState, state => state.loadedLanguages || {});

View file

@ -9,11 +9,9 @@ import thunk from 'redux-thunk';
import { createMemoryHistory, createBrowserHistory } from 'history';
import { routerMiddleware } from 'connected-react-router';
import createRootReducer from './reducers';
import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS, SETTINGS } from 'lbry-redux';
import { doGetSync } from 'lbryinc';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import { getSavedPassword, getAuthToken } from 'util/saved-passwords';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
import { doSyncSubscribe } from 'redux/actions/syncwrapper';
import { getAuthToken } from 'util/saved-passwords';
import { generateInitialUrl } from 'util/url';
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
@ -152,14 +150,7 @@ const sharedStateFilters = {
};
const sharedStateCb = ({ dispatch, getState }) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const emailVerified = selectUserVerifiedEmail(state);
if (syncEnabled && emailVerified) {
getSavedPassword().then(savedPassword => {
dispatch(doGetSync(savedPassword));
});
}
dispatch(doSyncSubscribe());
};
const populateAuthTokenHeader = () => {

View file

@ -6411,17 +6411,17 @@ lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#210bb80f2c49f6a166a8adc602a68b7490d5d23a:
lbry-redux@lbryio/lbry-redux#760623f99c90407ca3f1d06f1cdd2506fb2e443a:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/210bb80f2c49f6a166a8adc602a68b7490d5d23a"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/760623f99c90407ca3f1d06f1cdd2506fb2e443a"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
uuid "^3.3.2"
lbryinc@lbryio/lbryinc#cff5dd60934c4c6080e135f47ebbece1548c658c:
lbryinc@lbryio/lbryinc#35df87d1e69e435e25fc12832b6b1b788f76baaa:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/cff5dd60934c4c6080e135f47ebbece1548c658c"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/35df87d1e69e435e25fc12832b6b1b788f76baaa"
dependencies:
reselect "^3.0.0"