sync changes
This commit is contained in:
parent
bb9bde1c19
commit
6c4842a111
29 changed files with 270 additions and 170 deletions
|
@ -136,8 +136,8 @@
|
||||||
"imagesloaded": "^4.1.4",
|
"imagesloaded": "^4.1.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#210bb80f2c49f6a166a8adc602a68b7490d5d23a",
|
"lbry-redux": "lbryio/lbry-redux#760623f99c90407ca3f1d06f1cdd2506fb2e443a",
|
||||||
"lbryinc": "lbryio/lbryinc#cff5dd60934c4c6080e135f47ebbece1548c658c",
|
"lbryinc": "lbryio/lbryinc#35df87d1e69e435e25fc12832b6b1b788f76baaa",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.14",
|
||||||
|
|
|
@ -5,13 +5,18 @@ import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user';
|
||||||
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
||||||
import { doFetchChannelListMine, SETTINGS } from 'lbry-redux';
|
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 { 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 {
|
import {
|
||||||
doDownloadUpgradeRequested,
|
doDownloadUpgradeRequested,
|
||||||
doSignIn,
|
doSignIn,
|
||||||
doSyncWithPreferences,
|
|
||||||
doGetAndPopulatePreferences,
|
doGetAndPopulatePreferences,
|
||||||
doAnalyticsTagSync,
|
doAnalyticsTagSync,
|
||||||
} from 'redux/actions/app';
|
} from 'redux/actions/app';
|
||||||
|
@ -22,14 +27,15 @@ const select = state => ({
|
||||||
accessToken: selectAccessToken(state),
|
accessToken: selectAccessToken(state),
|
||||||
theme: selectThemePath(state),
|
theme: selectThemePath(state),
|
||||||
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
|
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
|
||||||
|
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
|
||||||
languages: selectLoadedLanguages(state),
|
languages: selectLoadedLanguages(state),
|
||||||
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
||||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||||
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
|
|
||||||
syncError: selectGetSyncErrorMessage(state),
|
syncError: selectGetSyncErrorMessage(state),
|
||||||
uploadCount: selectUploadCount(state),
|
uploadCount: selectUploadCount(state),
|
||||||
rewards: selectUnclaimedRewards(state),
|
rewards: selectUnclaimedRewards(state),
|
||||||
isAuthenticated: selectUserVerifiedEmail(state),
|
isAuthenticated: selectUserVerifiedEmail(state),
|
||||||
|
signInSyncPref: selectSyncSigninPref(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
@ -39,8 +45,9 @@ const perform = dispatch => ({
|
||||||
setLanguage: language => dispatch(doSetLanguage(language)),
|
setLanguage: language => dispatch(doSetLanguage(language)),
|
||||||
signIn: () => dispatch(doSignIn()),
|
signIn: () => dispatch(doSignIn()),
|
||||||
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
||||||
checkSync: () => dispatch(doSyncWithPreferences()),
|
|
||||||
updatePreferences: () => dispatch(doGetAndPopulatePreferences()),
|
updatePreferences: () => dispatch(doGetAndPopulatePreferences()),
|
||||||
|
updateSyncPref: () => dispatch(doUpdateSyncPref()),
|
||||||
|
syncSubscribe: () => dispatch(doSyncSubscribe()),
|
||||||
setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)),
|
setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ export const MAIN_WRAPPER_CLASS = 'main-wrapper';
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
export const IS_MAC = process.platform === 'darwin';
|
export const IS_MAC = process.platform === 'darwin';
|
||||||
// @endif
|
// @endif
|
||||||
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
|
|
||||||
|
|
||||||
// button numbers pulled from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
// button numbers pulled from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||||
const MOUSE_BACK_BTN = 3;
|
const MOUSE_BACK_BTN = 3;
|
||||||
|
@ -67,17 +66,20 @@ type Props = {
|
||||||
setLanguage: string => void,
|
setLanguage: string => void,
|
||||||
isUpgradeAvailable: boolean,
|
isUpgradeAvailable: boolean,
|
||||||
autoUpdateDownloaded: boolean,
|
autoUpdateDownloaded: boolean,
|
||||||
checkSync: () => void,
|
updatePreferences: () => Promise<any>,
|
||||||
updatePreferences: () => void,
|
updateSyncPref: () => void,
|
||||||
syncEnabled: boolean,
|
|
||||||
uploadCount: number,
|
uploadCount: number,
|
||||||
balance: ?number,
|
balance: ?number,
|
||||||
syncError: ?string,
|
syncError: ?string,
|
||||||
|
syncEnabled: boolean,
|
||||||
rewards: Array<Reward>,
|
rewards: Array<Reward>,
|
||||||
setReferrer: (string, boolean) => void,
|
setReferrer: (string, boolean) => void,
|
||||||
analyticsTagSync: () => void,
|
analyticsTagSync: () => void,
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean,
|
||||||
socketConnect: () => void,
|
socketConnect: () => void,
|
||||||
|
syncSubscribe: () => void,
|
||||||
|
syncEnabled: boolean,
|
||||||
|
signInSyncPref: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function App(props: Props) {
|
function App(props: Props) {
|
||||||
|
@ -90,8 +92,6 @@ function App(props: Props) {
|
||||||
autoUpdateDownloaded,
|
autoUpdateDownloaded,
|
||||||
isUpgradeAvailable,
|
isUpgradeAvailable,
|
||||||
requestDownloadUpgrade,
|
requestDownloadUpgrade,
|
||||||
syncEnabled,
|
|
||||||
checkSync,
|
|
||||||
uploadCount,
|
uploadCount,
|
||||||
history,
|
history,
|
||||||
syncError,
|
syncError,
|
||||||
|
@ -99,15 +99,19 @@ function App(props: Props) {
|
||||||
languages,
|
languages,
|
||||||
setLanguage,
|
setLanguage,
|
||||||
updatePreferences,
|
updatePreferences,
|
||||||
|
updateSyncPref,
|
||||||
rewards,
|
rewards,
|
||||||
setReferrer,
|
setReferrer,
|
||||||
analyticsTagSync,
|
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
|
syncSubscribe,
|
||||||
|
signInSyncPref,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const appRef = useRef();
|
const appRef = useRef();
|
||||||
const isEnhancedLayout = useKonamiListener();
|
const isEnhancedLayout = useKonamiListener();
|
||||||
const [hasSignedIn, setHasSignedIn] = useState(false);
|
const [hasSignedIn, setHasSignedIn] = useState(false);
|
||||||
|
const [readyForSync, setReadyForSync] = useState(false);
|
||||||
|
const [readyForPrefs, setReadyForPrefs] = useState(false);
|
||||||
const hasVerifiedEmail = user && user.has_verified_email;
|
const hasVerifiedEmail = user && user.has_verified_email;
|
||||||
const isRewardApproved = user && user.is_reward_approved;
|
const isRewardApproved = user && user.is_reward_approved;
|
||||||
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
|
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
|
||||||
|
@ -229,35 +233,40 @@ function App(props: Props) {
|
||||||
}
|
}
|
||||||
}, [previousRewardApproved, isRewardApproved]);
|
}, [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'
|
// @if TARGET='app'
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updatePreferences();
|
if (updatePreferences && readyForPrefs) {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
updatePreferences().then(() => {
|
||||||
}, []);
|
setReadyForSync(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [updatePreferences, setReadyForSync, readyForPrefs, hasVerifiedEmail]);
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
|
// ready for sync syncs, however after signin when hasVerifiedEmail, that syncs too.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasVerifiedEmail && syncEnabled) {
|
if (readyForSync && hasVerifiedEmail) {
|
||||||
checkSync();
|
// Copy sync checkbox to settings and push to preferences
|
||||||
analyticsTagSync();
|
// before sync if false, after sync if true so as not to change timestamp.
|
||||||
let syncInterval = setInterval(() => {
|
if (signInSyncPref === false) {
|
||||||
checkSync();
|
updateSyncPref();
|
||||||
}, SYNC_INTERVAL);
|
}
|
||||||
|
syncSubscribe();
|
||||||
return () => {
|
if (signInSyncPref === true) {
|
||||||
clearInterval(syncInterval);
|
updateSyncPref();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// 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(() => {
|
useEffect(() => {
|
||||||
if (syncError && isAuthenticated) {
|
if (syncError && isAuthenticated) {
|
||||||
|
@ -266,6 +275,15 @@ function App(props: Props) {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [syncError, pathname, isAuthenticated]);
|
}, [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'
|
// @if TARGET='web'
|
||||||
useDegradedPerformance(setLbryTvApiStatus);
|
useDegradedPerformance(setLbryTvApiStatus);
|
||||||
// @endif
|
// @endif
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { selectBalance, formatCredits, SETTINGS } from 'lbry-redux';
|
||||||
import { selectGetSyncErrorMessage } from 'lbryinc';
|
import { selectGetSyncErrorMessage } from 'lbryinc';
|
||||||
import { selectUserVerifiedEmail, selectUserEmail, selectEmailToVerify } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail, selectUserEmail, selectEmailToVerify } from 'redux/selectors/user';
|
||||||
import { doClearEmailEntry, doClearPasswordEntry } from 'redux/actions/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 { doSignOut, doOpenModal } from 'redux/actions/app';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import Header from './view';
|
import Header from './view';
|
||||||
|
@ -25,8 +25,7 @@ const select = state => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value, push) => dispatch(doSetClientSetting(key, value, push)),
|
||||||
syncSettings: () => dispatch(doSyncClientSettings()),
|
|
||||||
signOut: () => dispatch(doSignOut()),
|
signOut: () => dispatch(doSignOut()),
|
||||||
openChannelCreate: () => dispatch(doOpenModal(MODALS.CREATE_CHANNEL)),
|
openChannelCreate: () => dispatch(doOpenModal(MODALS.CREATE_CHANNEL)),
|
||||||
openSignOutModal: () => dispatch(doOpenModal(MODALS.SIGN_OUT)),
|
openSignOutModal: () => dispatch(doOpenModal(MODALS.SIGN_OUT)),
|
||||||
|
|
|
@ -37,7 +37,7 @@ type Props = {
|
||||||
},
|
},
|
||||||
currentTheme: string,
|
currentTheme: string,
|
||||||
automaticDarkModeEnabled: boolean,
|
automaticDarkModeEnabled: boolean,
|
||||||
setClientSetting: (string, boolean | string) => void,
|
setClientSetting: (string, boolean | string, ?boolean) => void,
|
||||||
hideBalance: boolean,
|
hideBalance: boolean,
|
||||||
email: ?string,
|
email: ?string,
|
||||||
authenticated: boolean,
|
authenticated: boolean,
|
||||||
|
@ -56,7 +56,6 @@ type Props = {
|
||||||
clearEmailEntry: () => void,
|
clearEmailEntry: () => void,
|
||||||
clearPasswordEntry: () => void,
|
clearPasswordEntry: () => void,
|
||||||
hasNavigated: boolean,
|
hasNavigated: boolean,
|
||||||
syncSettings: () => void,
|
|
||||||
sidebarOpen: boolean,
|
sidebarOpen: boolean,
|
||||||
setSidebarOpen: boolean => void,
|
setSidebarOpen: boolean => void,
|
||||||
isAbsoluteSideNavHidden: boolean,
|
isAbsoluteSideNavHidden: boolean,
|
||||||
|
@ -80,7 +79,6 @@ const Header = (props: Props) => {
|
||||||
clearPasswordEntry,
|
clearPasswordEntry,
|
||||||
emailToVerify,
|
emailToVerify,
|
||||||
backout,
|
backout,
|
||||||
syncSettings,
|
|
||||||
sidebarOpen,
|
sidebarOpen,
|
||||||
setSidebarOpen,
|
setSidebarOpen,
|
||||||
isAbsoluteSideNavHidden,
|
isAbsoluteSideNavHidden,
|
||||||
|
@ -149,11 +147,10 @@ const Header = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTheme === 'dark') {
|
if (currentTheme === 'dark') {
|
||||||
setClientSetting(SETTINGS.THEME, 'light');
|
setClientSetting(SETTINGS.THEME, 'light', true);
|
||||||
} else {
|
} else {
|
||||||
setClientSetting(SETTINGS.THEME, 'dark');
|
setClientSetting(SETTINGS.THEME, 'dark', true);
|
||||||
}
|
}
|
||||||
syncSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWalletTitle() {
|
function getWalletTitle() {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectPublishFormValues, doUpdatePublishForm } from 'lbry-redux';
|
import { selectPublishFormValues, doUpdatePublishForm } from 'lbry-redux';
|
||||||
import PublishPage from './view';
|
import PublishPage from './view';
|
||||||
import { selectUser, selectAccessToken } from '../../redux/selectors/user';
|
import { selectUser, selectAccessToken } from 'redux/selectors/user';
|
||||||
import {doFetchAccessToken} from '../../redux/actions/user';
|
import { doFetchAccessToken } from 'redux/actions/user';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
...selectPublishFormValues(state),
|
...selectPublishFormValues(state),
|
||||||
|
|
|
@ -3,8 +3,7 @@ import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectHasNavigated, selectScrollStartingPosition, selectWelcomeVersion } from 'redux/selectors/app';
|
import { selectHasNavigated, selectScrollStartingPosition, selectWelcomeVersion } from 'redux/selectors/app';
|
||||||
import Router from './view';
|
import Router from './view';
|
||||||
import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux';
|
import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux';
|
||||||
import { doSetHasNavigated, doSyncWithPreferences } from 'redux/actions/app';
|
import { doSetHasNavigated } from 'redux/actions/app';
|
||||||
import { doSyncClientSettings } from 'redux/actions/settings';
|
|
||||||
const select = state => {
|
const select = state => {
|
||||||
const { pathname, hash } = state.router.location;
|
const { pathname, hash } = state.router.location;
|
||||||
const urlPath = pathname + hash;
|
const urlPath = pathname + hash;
|
||||||
|
@ -34,8 +33,6 @@ const select = state => {
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
setHasNavigated: () => dispatch(doSetHasNavigated()),
|
setHasNavigated: () => dispatch(doSetHasNavigated()),
|
||||||
syncSettings: () => dispatch(doSyncClientSettings()),
|
|
||||||
checkSync: () => dispatch(doSyncWithPreferences()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(Router);
|
export default connect(select, perform)(Router);
|
||||||
|
|
|
@ -97,8 +97,6 @@ type Props = {
|
||||||
welcomeVersion: number,
|
welcomeVersion: number,
|
||||||
hasNavigated: boolean,
|
hasNavigated: boolean,
|
||||||
setHasNavigated: () => void,
|
setHasNavigated: () => void,
|
||||||
syncSettings: () => void,
|
|
||||||
checkSync: () => void,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function AppRouter(props: Props) {
|
function AppRouter(props: Props) {
|
||||||
|
@ -112,14 +110,11 @@ function AppRouter(props: Props) {
|
||||||
welcomeVersion,
|
welcomeVersion,
|
||||||
hasNavigated,
|
hasNavigated,
|
||||||
setHasNavigated,
|
setHasNavigated,
|
||||||
syncSettings,
|
|
||||||
checkSync,
|
|
||||||
} = props;
|
} = props;
|
||||||
const { entries } = history;
|
const { entries } = history;
|
||||||
const entryIndex = history.index;
|
const entryIndex = history.index;
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const resetScroll = urlParams.get('reset_scroll');
|
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"
|
// For people arriving at settings page from deeplinks, know whether they can "go back"
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -131,27 +126,6 @@ function AppRouter(props: Props) {
|
||||||
return unlisten;
|
return unlisten;
|
||||||
}, [hasNavigated, setHasNavigated]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (uri) {
|
if (uri) {
|
||||||
const { channelName, streamName } = parseURI(uri);
|
const { channelName, streamName } = parseURI(uri);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
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 { selectUserEmail } from 'redux/selectors/user';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import { doSignOut, doHandleSyncComplete } from 'redux/actions/app';
|
import { doSignOut, doHandleSyncComplete } from 'redux/actions/app';
|
||||||
|
@ -12,7 +13,7 @@ const select = state => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
getSync: (password, cb) => dispatch(doGetSync(password, cb)),
|
getSync: (cb, password) => dispatch(doGetSyncDesktop(cb, password)),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
handleSyncComplete: (error, hasDataChanged) => dispatch(doHandleSyncComplete(error, hasDataChanged)),
|
handleSyncComplete: (error, hasDataChanged) => dispatch(doHandleSyncComplete(error, hasDataChanged)),
|
||||||
signOut: () => dispatch(doSignOut()),
|
signOut: () => dispatch(doSignOut()),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import usePersistedState from 'effects/use-persisted-state';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
getSync: (?string, (any, boolean) => void) => void,
|
getSync: ((any, boolean) => void, ?string) => void,
|
||||||
getSyncIsPending: boolean,
|
getSyncIsPending: boolean,
|
||||||
email: string,
|
email: string,
|
||||||
passwordError: boolean,
|
passwordError: boolean,
|
||||||
|
@ -22,13 +22,13 @@ function SyncPassword(props: Props) {
|
||||||
const [rememberPassword, setRememberPassword] = usePersistedState(true);
|
const [rememberPassword, setRememberPassword] = usePersistedState(true);
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
getSync(password, (error, hasDataChanged) => {
|
getSync((error, hasDataChanged) => {
|
||||||
handleSyncComplete(error, hasDataChanged);
|
handleSyncComplete(error, hasDataChanged);
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
setSavedPassword(password, rememberPassword);
|
setSavedPassword(password, rememberPassword);
|
||||||
}
|
}
|
||||||
});
|
}, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
selectUser,
|
selectUser,
|
||||||
} from 'redux/selectors/user';
|
} from 'redux/selectors/user';
|
||||||
import { DAEMON_SETTINGS, SETTINGS } from 'lbry-redux';
|
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 { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
import UserEmailNew from './view';
|
import UserEmailNew from './view';
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ const select = state => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
setSync: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)),
|
setSync: value => dispatch(doSetSyncPref(value)),
|
||||||
setShareDiagnosticData: shouldShareData =>
|
setShareDiagnosticData: shouldShareData =>
|
||||||
dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, shouldShareData)),
|
dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, shouldShareData)),
|
||||||
doSignUp: (email, password) => dispatch(doUserSignUp(email, password)),
|
doSignUp: (email, password) => dispatch(doUserSignUp(email, password)),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
selectEmailNewIsPending,
|
selectEmailNewIsPending,
|
||||||
} from 'redux/selectors/user';
|
} from 'redux/selectors/user';
|
||||||
import { doUserCheckIfEmailExists, doClearEmailEntry } from 'redux/actions/user';
|
import { doUserCheckIfEmailExists, doClearEmailEntry } from 'redux/actions/user';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetSyncPref } from 'redux/actions/settings';
|
||||||
import UserEmailReturning from './view';
|
import UserEmailReturning from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -23,5 +23,5 @@ const select = state => ({
|
||||||
export default connect(select, {
|
export default connect(select, {
|
||||||
doUserCheckIfEmailExists,
|
doUserCheckIfEmailExists,
|
||||||
doClearEmailEntry,
|
doClearEmailEntry,
|
||||||
doSetClientSetting,
|
doSetSyncPref,
|
||||||
})(UserEmailReturning);
|
})(UserEmailReturning);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { SETTINGS } from 'lbry-redux';
|
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FormField, Form } from 'component/common/form';
|
import { FormField, Form } from 'component/common/form';
|
||||||
|
@ -18,7 +17,8 @@ type Props = {
|
||||||
doClearEmailEntry: () => void,
|
doClearEmailEntry: () => void,
|
||||||
doUserSignIn: (string, ?string) => void,
|
doUserSignIn: (string, ?string) => void,
|
||||||
doUserCheckIfEmailExists: string => void,
|
doUserCheckIfEmailExists: string => void,
|
||||||
doSetClientSetting: (string, boolean) => void,
|
doSetSyncPref: boolean => void,
|
||||||
|
doSetClientSetting: (string, boolean, ?boolean) => void,
|
||||||
isPending: boolean,
|
isPending: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ function UserEmailReturning(props: Props) {
|
||||||
emailToVerify,
|
emailToVerify,
|
||||||
doClearEmailEntry,
|
doClearEmailEntry,
|
||||||
emailDoesNotExist,
|
emailDoesNotExist,
|
||||||
doSetClientSetting,
|
doSetSyncPref,
|
||||||
isPending,
|
isPending,
|
||||||
} = props;
|
} = props;
|
||||||
const { push, location } = useHistory();
|
const { push, location } = useHistory();
|
||||||
|
@ -48,7 +48,7 @@ function UserEmailReturning(props: Props) {
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
doSetClientSetting(SETTINGS.ENABLE_SYNC, syncEnabled);
|
doSetSyncPref(syncEnabled);
|
||||||
// @endif
|
// @endif
|
||||||
doUserCheckIfEmailExists(email);
|
doUserCheckIfEmailExists(email);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Spinner from 'component/spinner';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: ?User,
|
user: ?User,
|
||||||
history: { push: string => void },
|
history: { push: string => void, replace: string => void },
|
||||||
location: { search: string },
|
location: { search: string },
|
||||||
userFetchPending: boolean,
|
userFetchPending: boolean,
|
||||||
doUserSignIn: string => void,
|
doUserSignIn: string => void,
|
||||||
|
@ -28,7 +28,7 @@ function UserSignIn(props: Props) {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (hasVerifiedEmail || (!showEmail && !showPassword && !showLoading)) {
|
if (hasVerifiedEmail || (!showEmail && !showPassword && !showLoading)) {
|
||||||
history.push(redirect || '/');
|
history.replace(redirect || '/');
|
||||||
}
|
}
|
||||||
}, [showEmail, showPassword, showLoading, hasVerifiedEmail]);
|
}, [showEmail, showPassword, showLoading, hasVerifiedEmail]);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const PASSWORD_SAVED = 'PASSWORD_SAVED';
|
||||||
export const SET_WELCOME_VERSION = 'SET_WELCOME_VERSION';
|
export const SET_WELCOME_VERSION = 'SET_WELCOME_VERSION';
|
||||||
export const SET_ALLOW_ANALYTICS = 'SET_ALLOW_ANALYTICS';
|
export const SET_ALLOW_ANALYTICS = 'SET_ALLOW_ANALYTICS';
|
||||||
export const SET_HAS_NAVIGATED = 'SET_HAS_NAVIGATED';
|
export const SET_HAS_NAVIGATED = 'SET_HAS_NAVIGATED';
|
||||||
|
export const SET_SYNC_LOCK = 'SET_SYNC_LOCK';
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
|
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_STARTED = 'FINDING_FFMPEG_STARTED';
|
||||||
export const FINDING_FFMPEG_COMPLETED = 'FINDING_FFMPEG_COMPLETED';
|
export const FINDING_FFMPEG_COMPLETED = 'FINDING_FFMPEG_COMPLETED';
|
||||||
export const SYNC_CLIENT_SETTINGS = 'SYNC_CLIENT_SETTINGS';
|
export const SYNC_CLIENT_SETTINGS = 'SYNC_CLIENT_SETTINGS';
|
||||||
|
export const SYNC_PREFERENCE_CHANGED = 'SYNC_PREFERENCE_CHANGED';
|
||||||
|
|
||||||
// User
|
// User
|
||||||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
||||||
|
|
28
ui/index.jsx
28
ui/index.jsx
|
@ -117,30 +117,10 @@ doAuthTokenRefresh();
|
||||||
// We keep a local variable for authToken because `ipcRenderer.send` does not
|
// 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
|
// contain a response, so there is no way to know when it's been set
|
||||||
let authToken;
|
let authToken;
|
||||||
Lbryio.setOverride(
|
Lbryio.setOverride('setAuthToken', authToken => {
|
||||||
'setAuthToken',
|
setAuthToken(authToken);
|
||||||
status =>
|
return authToken;
|
||||||
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(
|
Lbryio.setOverride(
|
||||||
'getAuthToken',
|
'getAuthToken',
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {
|
||||||
doClearDaemonSetting,
|
doClearDaemonSetting,
|
||||||
doSetClientSetting,
|
doSetClientSetting,
|
||||||
doSetDarkTime,
|
doSetDarkTime,
|
||||||
doSyncClientSettings,
|
doEnterSettingsPage,
|
||||||
|
doExitSettingsPage,
|
||||||
} from 'redux/actions/settings';
|
} from 'redux/actions/settings';
|
||||||
import { doSetPlayingUri } from 'redux/actions/content';
|
import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
|
@ -37,7 +38,6 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
||||||
syncSettings: () => dispatch(doSyncClientSettings()),
|
|
||||||
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
|
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
|
||||||
toggle3PAnalytics: allow => dispatch(doToggle3PAnalytics(allow)),
|
toggle3PAnalytics: allow => dispatch(doToggle3PAnalytics(allow)),
|
||||||
clearCache: () => dispatch(doClearCache()),
|
clearCache: () => dispatch(doClearCache()),
|
||||||
|
@ -47,6 +47,8 @@ const perform = dispatch => ({
|
||||||
clearPlayingUri: () => dispatch(doSetPlayingUri(null)),
|
clearPlayingUri: () => dispatch(doSetPlayingUri(null)),
|
||||||
setDarkTime: (time, options) => dispatch(doSetDarkTime(time, options)),
|
setDarkTime: (time, options) => dispatch(doSetDarkTime(time, options)),
|
||||||
openModal: (id, params) => dispatch(doOpenModal(id, params)),
|
openModal: (id, params) => dispatch(doOpenModal(id, params)),
|
||||||
|
enterSettings: () => dispatch(doEnterSettingsPage()),
|
||||||
|
exitSettings: () => dispatch(doExitSettingsPage()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(SettingsPage);
|
export default connect(select, perform)(SettingsPage);
|
||||||
|
|
|
@ -4,7 +4,6 @@ import * as MODALS from 'constants/modal_types';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
import { SETTINGS } from 'lbry-redux';
|
||||||
|
|
||||||
import { FormField } from 'component/common/form';
|
import { FormField } from 'component/common/form';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
|
@ -66,6 +65,8 @@ type Props = {
|
||||||
openModal: string => void,
|
openModal: string => void,
|
||||||
language?: string,
|
language?: string,
|
||||||
syncEnabled: boolean,
|
syncEnabled: boolean,
|
||||||
|
enterSettings: () => void,
|
||||||
|
exitSettings: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -89,7 +90,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { isAuthenticated } = this.props;
|
const { isAuthenticated, enterSettings } = this.props;
|
||||||
|
|
||||||
if (isAuthenticated || !IS_WEB) {
|
if (isAuthenticated || !IS_WEB) {
|
||||||
this.props.updateWalletStatus();
|
this.props.updateWalletStatus();
|
||||||
|
@ -99,6 +100,12 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
enterSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { exitSettings } = this.props;
|
||||||
|
exitSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
onThemeChange(event: SyntheticInputEvent<*>) {
|
onThemeChange(event: SyntheticInputEvent<*>) {
|
||||||
|
@ -211,11 +218,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
<Card
|
<Card
|
||||||
title={__('Sync')}
|
title={__('Sync')}
|
||||||
subtitle={
|
subtitle={
|
||||||
walletEncrypted && !storedPassword
|
walletEncrypted && !storedPassword && storedPassword !== ''
|
||||||
? __("To enable this feature, check 'Save Password' the next time you start the app.")
|
? __("To enable Sync, close LBRY completely and check 'Remember Password' during wallet unlock.")
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
actions={<SyncToggle disabled={walletEncrypted && !storedPassword} />}
|
actions={<SyncToggle disabled={walletEncrypted && !storedPassword && storedPassword !== ''} />}
|
||||||
/>
|
/>
|
||||||
{/* @endif */}
|
{/* @endif */}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {
|
||||||
doClearDaemonSetting,
|
doClearDaemonSetting,
|
||||||
doSetClientSetting,
|
doSetClientSetting,
|
||||||
doFindFFmpeg,
|
doFindFFmpeg,
|
||||||
doSyncClientSettings,
|
doEnterSettingsPage,
|
||||||
|
doExitSettingsPage,
|
||||||
} from 'redux/actions/settings';
|
} from 'redux/actions/settings';
|
||||||
import {
|
import {
|
||||||
makeSelectClientSetting,
|
makeSelectClientSetting,
|
||||||
|
@ -34,7 +35,6 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
||||||
syncSettings: () => dispatch(doSyncClientSettings()),
|
|
||||||
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
|
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
|
||||||
clearCache: () => dispatch(doClearCache()),
|
clearCache: () => dispatch(doClearCache()),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
|
@ -43,6 +43,8 @@ const perform = dispatch => ({
|
||||||
updateWalletStatus: () => dispatch(doWalletStatus()),
|
updateWalletStatus: () => dispatch(doWalletStatus()),
|
||||||
confirmForgetPassword: modalProps => dispatch(doNotifyForgetPassword(modalProps)),
|
confirmForgetPassword: modalProps => dispatch(doNotifyForgetPassword(modalProps)),
|
||||||
findFFmpeg: () => dispatch(doFindFFmpeg()),
|
findFFmpeg: () => dispatch(doFindFFmpeg()),
|
||||||
|
enterSettings: () => dispatch(doEnterSettingsPage()),
|
||||||
|
exitSettings: () => dispatch(doExitSettingsPage()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(SettingsPage);
|
export default connect(select, perform)(SettingsPage);
|
||||||
|
|
|
@ -55,6 +55,8 @@ type Props = {
|
||||||
language?: string,
|
language?: string,
|
||||||
syncEnabled: boolean,
|
syncEnabled: boolean,
|
||||||
syncSettings: () => void,
|
syncSettings: () => void,
|
||||||
|
enterSettings: () => void,
|
||||||
|
exitSettings: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -81,7 +83,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { isAuthenticated, ffmpegStatus, daemonSettings, findFFmpeg } = this.props;
|
const { isAuthenticated, ffmpegStatus, daemonSettings, findFFmpeg, enterSettings } = this.props;
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
const { available } = ffmpegStatus;
|
const { available } = ffmpegStatus;
|
||||||
|
@ -102,6 +104,12 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
enterSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { exitSettings } = this.props;
|
||||||
|
exitSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
onFFmpegFolder(path: string) {
|
onFFmpegFolder(path: string) {
|
||||||
|
|
|
@ -46,11 +46,11 @@ import {
|
||||||
import { selectDaemonSettings } from 'redux/selectors/settings';
|
import { selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
import { selectUser } from 'redux/selectors/user';
|
import { selectUser } from 'redux/selectors/user';
|
||||||
// import { selectDaemonSettings } from 'redux/selectors/settings';
|
// import { selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
import { doGetSync } from 'lbryinc';
|
import { doSyncSubscribe } from 'redux/actions/syncwrapper';
|
||||||
import { doAuthenticate } from 'redux/actions/user';
|
import { doAuthenticate } from 'redux/actions/user';
|
||||||
import { lbrySettings as config, version as appVersion } from 'package.json';
|
import { lbrySettings as config, version as appVersion } from 'package.json';
|
||||||
import analytics, { SHARE_INTERNAL } from 'analytics';
|
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 { doSocketConnect } from 'redux/actions/websocket';
|
||||||
import { stringifyServerParam, shouldSetSetting } from 'util/sync-settings';
|
import { stringifyServerParam, shouldSetSetting } from 'util/sync-settings';
|
||||||
import sha256 from 'crypto-js/sha256';
|
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() {
|
export function doSkipUpgrade() {
|
||||||
return {
|
return {
|
||||||
type: ACTIONS.SKIP_UPGRADE,
|
type: ACTIONS.SKIP_UPGRADE,
|
||||||
|
@ -622,6 +629,7 @@ export function doGetAndPopulatePreferences() {
|
||||||
}
|
}
|
||||||
// @endif
|
// @endif
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function failCb() {
|
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() {
|
export function doSyncWithPreferences() {
|
||||||
return dispatch => {
|
return dispatch => dispatch(doSyncSubscribe());
|
||||||
return getSavedPassword().then(password => {
|
|
||||||
const passwordArgument = password === null ? '' : password;
|
|
||||||
|
|
||||||
dispatch(doGetSync(passwordArgument, (error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ import analytics from 'analytics';
|
||||||
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
||||||
import { launcher } from 'util/autoLaunch';
|
import { launcher } from 'util/autoLaunch';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
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 { DEFAULT_LANGUAGE } = require('config');
|
||||||
const { SDK_SYNC_KEYS } = SHARED_PREFERENCES;
|
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 => {
|
return dispatch => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
||||||
|
@ -128,6 +131,30 @@ export function doSetClientSetting(key, value) {
|
||||||
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() {
|
export function doPushSettingsToPrefs() {
|
||||||
return (dispatch, getState) => {
|
return dispatch => {
|
||||||
const state = getState();
|
return new Promise((resolve, reject) => {
|
||||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
|
||||||
if (syncEnabled) {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LOCAL_ACTIONS.SYNC_CLIENT_SETTINGS,
|
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());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
57
ui/redux/actions/syncwrapper.js
Normal file
57
ui/redux/actions/syncwrapper.js
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ export type AppState = {
|
||||||
welcomeVersion: number,
|
welcomeVersion: number,
|
||||||
allowAnalytics: boolean,
|
allowAnalytics: boolean,
|
||||||
hasNavigated: boolean,
|
hasNavigated: boolean,
|
||||||
|
syncLocked: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultState: AppState = {
|
const defaultState: AppState = {
|
||||||
|
@ -76,6 +77,7 @@ const defaultState: AppState = {
|
||||||
welcomeVersion: 0.0,
|
welcomeVersion: 0.0,
|
||||||
allowAnalytics: false,
|
allowAnalytics: false,
|
||||||
hasNavigated: false,
|
hasNavigated: false,
|
||||||
|
syncLocked: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// @@router comes from react-router
|
// @@router comes from react-router
|
||||||
|
@ -109,6 +111,11 @@ reducers[ACTIONS.DAEMON_READY] = state =>
|
||||||
daemonReady: true,
|
daemonReady: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_LOCK] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncLocked: action.data,
|
||||||
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.PASSWORD_SAVED] = (state, action) =>
|
reducers[ACTIONS.PASSWORD_SAVED] = (state, action) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
isPasswordSaved: action.data,
|
isPasswordSaved: action.data,
|
||||||
|
|
|
@ -24,6 +24,7 @@ const defaultState = {
|
||||||
findingFFmpeg: false,
|
findingFFmpeg: false,
|
||||||
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
|
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
|
||||||
customWalletServers: [],
|
customWalletServers: [],
|
||||||
|
syncEnabledPref: undefined, // set this during sign in, copy it to clientSettings immediately after prefGet after signedin but before sync
|
||||||
sharedPreferences: {},
|
sharedPreferences: {},
|
||||||
daemonSettings: {},
|
daemonSettings: {},
|
||||||
daemonStatus: { ffmpeg_status: {} },
|
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 => {
|
reducers[ACTIONS.SYNC_CLIENT_SETTINGS] = state => {
|
||||||
const { clientSettings } = state;
|
const { clientSettings } = state;
|
||||||
const sharedPreferences = Object.assign({}, state.sharedPreferences);
|
const sharedPreferences = Object.assign({}, state.sharedPreferences);
|
||||||
|
@ -163,6 +153,12 @@ reducers[ACTIONS.SYNC_CLIENT_SETTINGS] = state => {
|
||||||
return Object.assign({}, state, { sharedPreferences: newSharedPreferences });
|
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) => {
|
reducers[LBRY_REDUX_ACTIONS.USER_STATE_POPULATE] = (state, action) => {
|
||||||
const { clientSettings: currentClientSettings } = state;
|
const { clientSettings: currentClientSettings } = state;
|
||||||
const { settings: sharedPreferences } = action.data;
|
const { settings: sharedPreferences } = action.data;
|
||||||
|
|
|
@ -80,3 +80,5 @@ export const selectAllowAnalytics = createSelector(selectState, state => state.a
|
||||||
export const selectScrollStartingPosition = createSelector(selectState, state => state.currentScroll);
|
export const selectScrollStartingPosition = createSelector(selectState, state => state.currentScroll);
|
||||||
|
|
||||||
export const selectIsPasswordSaved = createSelector(selectState, state => state.isPasswordSaved);
|
export const selectIsPasswordSaved = createSelector(selectState, state => state.isPasswordSaved);
|
||||||
|
|
||||||
|
export const selectSyncIsLocked = createSelector(selectState, state => state.syncLocked);
|
||||||
|
|
|
@ -11,6 +11,8 @@ export const selectFfmpegStatus = createSelector(selectDaemonStatus, status => s
|
||||||
|
|
||||||
export const selectFindingFFmpeg = createSelector(selectState, state => state.findingFFmpeg || false);
|
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 selectClientSettings = createSelector(selectState, state => state.clientSettings || {});
|
||||||
|
|
||||||
export const selectLoadedLanguages = createSelector(selectState, state => state.loadedLanguages || {});
|
export const selectLoadedLanguages = createSelector(selectState, state => state.loadedLanguages || {});
|
||||||
|
|
17
ui/store.js
17
ui/store.js
|
@ -9,11 +9,9 @@ import thunk from 'redux-thunk';
|
||||||
import { createMemoryHistory, createBrowserHistory } from 'history';
|
import { createMemoryHistory, createBrowserHistory } from 'history';
|
||||||
import { routerMiddleware } from 'connected-react-router';
|
import { routerMiddleware } from 'connected-react-router';
|
||||||
import createRootReducer from './reducers';
|
import createRootReducer from './reducers';
|
||||||
import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS, SETTINGS } from 'lbry-redux';
|
import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
|
||||||
import { doGetSync } from 'lbryinc';
|
import { doSyncSubscribe } from 'redux/actions/syncwrapper';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { getAuthToken } from 'util/saved-passwords';
|
||||||
import { getSavedPassword, getAuthToken } from 'util/saved-passwords';
|
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
|
||||||
import { generateInitialUrl } from 'util/url';
|
import { generateInitialUrl } from 'util/url';
|
||||||
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
||||||
|
|
||||||
|
@ -152,14 +150,7 @@ const sharedStateFilters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const sharedStateCb = ({ dispatch, getState }) => {
|
const sharedStateCb = ({ dispatch, getState }) => {
|
||||||
const state = getState();
|
dispatch(doSyncSubscribe());
|
||||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
|
||||||
const emailVerified = selectUserVerifiedEmail(state);
|
|
||||||
if (syncEnabled && emailVerified) {
|
|
||||||
getSavedPassword().then(savedPassword => {
|
|
||||||
dispatch(doGetSync(savedPassword));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const populateAuthTokenHeader = () => {
|
const populateAuthTokenHeader = () => {
|
||||||
|
|
|
@ -6411,17 +6411,17 @@ lazy-val@^1.0.4:
|
||||||
yargs "^13.2.2"
|
yargs "^13.2.2"
|
||||||
zstd-codec "^0.1.1"
|
zstd-codec "^0.1.1"
|
||||||
|
|
||||||
lbry-redux@lbryio/lbry-redux#210bb80f2c49f6a166a8adc602a68b7490d5d23a:
|
lbry-redux@lbryio/lbry-redux#760623f99c90407ca3f1d06f1cdd2506fb2e443a:
|
||||||
version "0.0.1"
|
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:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
lbryinc@lbryio/lbryinc#cff5dd60934c4c6080e135f47ebbece1548c658c:
|
lbryinc@lbryio/lbryinc#35df87d1e69e435e25fc12832b6b1b788f76baaa:
|
||||||
version "0.0.1"
|
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:
|
dependencies:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue