diff --git a/package.json b/package.json index 4c15ae90f..49c1f6def 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "imagesloaded": "^4.1.4", "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", - "lbry-redux": "lbryio/lbry-redux#437c54f164b4132c0d5c6ede60c7b02703f1e9d5", + "lbry-redux": "lbryio/lbry-redux#e10295b5ec2ef42426755ed9fd50bd250b0d76dd", "lbryinc": "lbryio/lbryinc#9440717a00d2fbb2e3226aaa2388f4698f324be2", "lint-staged": "^7.0.2", "localforage": "^1.7.1", diff --git a/static/app-strings.json b/static/app-strings.json index 82e1a7671..eb66cc53a 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1292,5 +1292,20 @@ "%comment_count% comments - %comment_count_change% this week": "%comment_count% comments - %comment_count_change% this week", "Invite": "Invite", "Remove File": "Remove File", + "You are currently following %followingCount% tag": "You are currently following %followingCount% tag", + "DO a thing": "DO a thing", + "DO IT": "DO IT", + "Not Yet": "Not Yet", + "I understand": "I understand", + "Enabling Sync": "Enabling Sync", + "Enable Sync": "Enable Sync", + "Bring my stuff!": "Bring my stuff!", + "Disable Sync": "Disable Sync", + "Enable": "Enable", + "Continuing will import the profile in your cloud wallet into your local wallet.": "Continuing will import the profile in your cloud wallet into your local wallet.", + "": "", + "Getting your profiles...": "Getting your profiles...", + "Enabling sync will switch to your cloud profile.": "Enabling sync will switch to your cloud profile.", + "Disabling sync will switch to your local profile.": "Disabling sync will switch to your local profile.", "--end--": "--end--" } diff --git a/ui/component/app/index.js b/ui/component/app/index.js index 2827ded9b..19850d29e 100644 --- a/ui/component/app/index.js +++ b/ui/component/app/index.js @@ -5,14 +5,9 @@ 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, - selectSyncSigninPref, - selectThemePath, -} from 'redux/selectors/settings'; +import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings'; import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app'; -import { doSetLanguage, doUpdateSyncPrefIfFalse } from 'redux/actions/settings'; +import { doGetWalletSyncPreference, doSetLanguage } from 'redux/actions/settings'; import { doSyncSubscribe } from 'redux/actions/syncwrapper'; import { doDownloadUpgradeRequested, @@ -35,7 +30,6 @@ const select = state => ({ uploadCount: selectUploadCount(state), rewards: selectUnclaimedRewards(state), isAuthenticated: selectUserVerifiedEmail(state), - signInSyncPref: selectSyncSigninPref(state), }); const perform = dispatch => ({ @@ -46,7 +40,7 @@ const perform = dispatch => ({ signIn: () => dispatch(doSignIn()), requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()), updatePreferences: () => dispatch(doGetAndPopulatePreferences()), - pushPrefsIfSyncFalse: () => dispatch(doUpdateSyncPrefIfFalse()), + getWalletSyncPref: () => dispatch(doGetWalletSyncPreference()), syncSubscribe: () => dispatch(doSyncSubscribe()), setReferrer: (referrer, doClaim) => dispatch(doUserSetReferrer(referrer, doClaim)), }); diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index d238a8427..a7d66b2af 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -67,7 +67,7 @@ type Props = { isUpgradeAvailable: boolean, autoUpdateDownloaded: boolean, updatePreferences: () => Promise, - pushPrefsIfSyncFalse: () => void, + getWalletSyncPref: () => Promise, uploadCount: number, balance: ?number, syncError: ?string, @@ -79,7 +79,6 @@ type Props = { socketConnect: () => void, syncSubscribe: () => void, syncEnabled: boolean, - signInSyncPref: boolean, }; function App(props: Props) { @@ -99,12 +98,11 @@ function App(props: Props) { languages, setLanguage, updatePreferences, - pushPrefsIfSyncFalse, + getWalletSyncPref, rewards, setReferrer, isAuthenticated, syncSubscribe, - signInSyncPref, } = props; const appRef = useRef(); @@ -112,7 +110,7 @@ function App(props: Props) { 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 && Boolean(user.has_verified_email); const isRewardApproved = user && user.is_reward_approved; const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail); const previousRewardApproved = usePrevious(isRewardApproved); @@ -235,34 +233,26 @@ function App(props: Props) { // @if TARGET='app' useEffect(() => { - if (updatePreferences && readyForPrefs) { - updatePreferences().then(() => { - // will pull and U_S_P; usp will make sure prefBox applied if false - setReadyForSync(true); - }); + if (updatePreferences && getWalletSyncPref && readyForPrefs) { + getWalletSyncPref() + .then(() => updatePreferences()) + .then(() => { + setReadyForSync(true); + }); } - }, [updatePreferences, setReadyForSync, readyForPrefs, hasVerifiedEmail]); + }, [updatePreferences, getWalletSyncPref, setReadyForSync, readyForPrefs, hasVerifiedEmail]); // @endif // ready for sync syncs, however after signin when hasVerifiedEmail, that syncs too. useEffect(() => { // signInSyncPref is cleared after sharedState loop. - if (readyForSync && hasVerifiedEmail && signInSyncPref === undefined) { - // On sign-in, we get and apply all the information on whether to sync - // the checkbox, previous wallet settings, store rehydrate, etc - // Our app is up to date with the wallet - // Because the checkbox is applied last, make sure the wallet remembers if false: - // @if TARGET='app' - pushPrefsIfSyncFalse(); - // @endif - - // And try this in case we are syncing. + if (readyForSync && hasVerifiedEmail) { + // In case we are syncing. syncSubscribe(); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [readyForSync, hasVerifiedEmail, signInSyncPref, pushPrefsIfSyncFalse, syncSubscribe]); + }, [readyForSync, hasVerifiedEmail, syncSubscribe]); - // We know someone is logging in or not when we get their user object {} + // 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(() => { diff --git a/ui/component/syncEnableFlow/index.js b/ui/component/syncEnableFlow/index.js new file mode 100644 index 000000000..f4a97bf87 --- /dev/null +++ b/ui/component/syncEnableFlow/index.js @@ -0,0 +1,34 @@ +import { SETTINGS } from 'lbry-redux'; +import { connect } from 'react-redux'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; +import { + selectGetSyncErrorMessage, + selectHasSyncedWallet, + selectGetSyncIsPending, + selectHashChanged, + doCheckSync, + doGetSync, +} from 'lbryinc'; +import { makeSelectClientSetting } from 'redux/selectors/settings'; +import { doSetWalletSyncPreference } from 'redux/actions/settings'; +import SyncToggle from './view'; +import { doGetAndPopulatePreferences } from 'redux/actions/app'; + +const select = state => ({ + syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state), + hasSyncedWallet: selectHasSyncedWallet(state), + hasSyncChanged: selectHashChanged(state), + verifiedEmail: selectUserVerifiedEmail(state), + getSyncError: selectGetSyncErrorMessage(state), + getSyncPending: selectGetSyncIsPending(state), + language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state), +}); + +const perform = dispatch => ({ + setSyncEnabled: value => dispatch(doSetWalletSyncPreference(value)), + checkSync: () => dispatch(doCheckSync()), + getSync: (pw, cb) => dispatch(doGetSync(pw, cb)), + updatePreferences: () => dispatch(doGetAndPopulatePreferences()), +}); + +export default connect(select, perform)(SyncToggle); diff --git a/ui/component/syncEnableFlow/view.jsx b/ui/component/syncEnableFlow/view.jsx new file mode 100644 index 000000000..3c12ba2c3 --- /dev/null +++ b/ui/component/syncEnableFlow/view.jsx @@ -0,0 +1,234 @@ +// @flow +import React from 'react'; +import Button from 'component/button'; +import { getSavedPassword } from 'util/saved-passwords'; +import Card from 'component/common/card'; +import { withRouter } from 'react-router'; +import Spinner from 'component/spinner'; +import { Lbry } from 'lbry-redux'; +import ErrorText from 'component/common/error-text'; + +type Props = { + setSyncEnabled: boolean => void, + syncEnabled: boolean, + getSyncError: ?string, + getSyncPending: boolean, + getSync: (pw: string, cb: () => void) => void, + checkSync: () => void, + closeModal: () => void, + updatePreferences: () => void, + mode: string, +}; + +const ENABLE_MODE = 'enable'; + +// steps +const FETCH_FOR_ENABLE = 'fetch-for-enable'; +const FETCH_FOR_DISABLE = 'fetch-for-disable'; +const CONFIRM = 'confirm'; +const INITIAL = 'initial'; +const ERROR = 'error'; + +const SHARED_KEY = 'shared'; +const LOCAL_KEY = 'local'; + +function SyncEnableFlow(props: Props) { + const { + setSyncEnabled, + getSyncError, + getSyncPending, + getSync, + checkSync, + mode, + closeModal, + updatePreferences, + } = props; + + const [step, setStep] = React.useState(INITIAL); + const [prefDict, setPrefDict]: [any, (any) => void] = React.useState(); + const [password, setPassword] = React.useState(''); + const [error, setError] = React.useState(); + + const handleSyncToggle = async () => { + const shared = prefDict.shared; + const local = prefDict.local; + let finalPrefs; + if (shared && local) { + if (mode === ENABLE_MODE) { + finalPrefs = makeMergedPrefs(local, shared); + } else { + finalPrefs = makeMergedPrefs(shared, local); + } + } else { + finalPrefs = local || shared || null; + } + + // set busy (disable button) + if (finalPrefs) { + await Lbry.preference_set({ key: mode === ENABLE_MODE ? SHARED_KEY : LOCAL_KEY, value: finalPrefs }); + } + await setSyncEnabled(mode === ENABLE_MODE); + await updatePreferences(); + closeModal(); + }; + + const makeMergedPrefs = (from, to) => { + const mergedTo = to; + const toPrefs = to.value; + const fromPrefs = from.value; + if (!fromPrefs) { + return to; + } + + const mergedBlockListSet = new Set(toPrefs.blocked || []); + const mergedSubscriptionsSet = new Set(toPrefs.subscriptions || []); + const mergedTagsSet = new Set(toPrefs.tags || []); + + const fromBlocklist = fromPrefs.blocked || []; + const fromSubscriptions = fromPrefs.subscriptions || []; + const fromTags = fromPrefs.tags || []; + + if (fromBlocklist.length) { + fromBlocklist.forEach(el => mergedBlockListSet.add(el)); + } + if (fromSubscriptions.length) { + fromSubscriptions.forEach(el => mergedSubscriptionsSet.add(el)); + } + if (fromTags.length) { + fromTags.forEach(el => mergedTagsSet.add(el)); + } + + toPrefs.blocked = Array.from(mergedBlockListSet); + toPrefs.subscriptions = Array.from(mergedSubscriptionsSet); + toPrefs.tags = Array.from(mergedTagsSet); + mergedTo.value = toPrefs; + return mergedTo; + }; + + React.useEffect(() => { + if (mode) { + checkSync(); + if (mode === ENABLE_MODE) { + setStep(FETCH_FOR_ENABLE); + } else { + setStep(FETCH_FOR_DISABLE); + } + } + }, [mode]); + + React.useEffect(() => { + getSavedPassword() + .then(pw => setPassword(pw || '')) + .catch(e => { + setError(e && e.message ? e.message : e); + }); + }, []); + + React.useEffect(() => { + if (step === FETCH_FOR_ENABLE) { + getSync(password, (e, hasChanged) => { + if (e) { + setStep(ERROR); + setError(e && e.message ? e.message : e); + } else { + Lbry.preference_get().then(result => { + const prefs = {}; + if (result[SHARED_KEY]) prefs[SHARED_KEY] = result[SHARED_KEY]; + if (result[LOCAL_KEY]) prefs[LOCAL_KEY] = result[LOCAL_KEY]; + setPrefDict(prefs); + setStep(CONFIRM); + }); + } + }); + } + if (step === FETCH_FOR_DISABLE) { + Lbry.preference_get().then(result => { + const prefs = {}; + if (result[SHARED_KEY]) prefs[SHARED_KEY] = result[SHARED_KEY]; + if (result[LOCAL_KEY]) prefs[LOCAL_KEY] = result[LOCAL_KEY]; + setPrefDict(prefs); + setStep(CONFIRM); + }); + } + }, [step, setPrefDict, setStep, password]); + + if (getSyncPending) { + return ( +
+ +
+ ); + } + + return ( + + {step === INITIAL && ( + <> +

{__(`Please wait...`)}

+ + + )} + {(step === FETCH_FOR_ENABLE || step === FETCH_FOR_DISABLE) && ( + <> +

{__(`Getting your profiles...`)}

+ + + )} + {step === CONFIRM && mode === ENABLE_MODE && ( + <> +

{__(`Enabling sync will switch to your cloud profile.`)}

+ + )} + {step === CONFIRM && mode !== ENABLE_MODE && ( + <> +

{__(`Disabling sync will switch to your local profile.`)}

+ + )} + {(error || getSyncError) && ( + <> +

{__(`Something went wrong...`)}

+ {error || (getSyncError && String(getSyncError)) || __('Unknown error')} + + )} + + } + actions={ + <> + {step === CONFIRM && ( +
+
+ )} + {(step === FETCH_FOR_ENABLE || step === FETCH_FOR_DISABLE) && ( +
+
+ )} + {(error || getSyncError) && ( +
+
+ )} + + } + /> + ); +} + +export default withRouter(SyncEnableFlow); diff --git a/ui/component/syncToggle/index.js b/ui/component/syncToggle/index.js index b94209c97..1fff69336 100644 --- a/ui/component/syncToggle/index.js +++ b/ui/component/syncToggle/index.js @@ -3,7 +3,8 @@ import { connect } from 'react-redux'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectGetSyncErrorMessage } from 'lbryinc'; import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { doSetClientSetting } from 'redux/actions/settings'; +import { doSetWalletSyncPreference } from 'redux/actions/settings'; +import { doOpenModal } from 'redux/actions/app'; import SyncToggle from './view'; const select = state => ({ @@ -14,7 +15,8 @@ const select = state => ({ }); const perform = dispatch => ({ - setSyncEnabled: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)), + setSyncEnabled: value => dispatch(doSetWalletSyncPreference(value)), + openModal: (id, props) => dispatch(doOpenModal(id, props)), }); export default connect(select, perform)(SyncToggle); diff --git a/ui/component/syncToggle/view.jsx b/ui/component/syncToggle/view.jsx index f20975b8a..1f85c0278 100644 --- a/ui/component/syncToggle/view.jsx +++ b/ui/component/syncToggle/view.jsx @@ -1,9 +1,10 @@ // @flow import * as PAGES from 'constants/pages'; +import * as MODALS from 'constants/modal_types'; import React from 'react'; import Button from 'component/button'; -import { FormField } from 'component/common/form'; import { withRouter } from 'react-router'; +import { FormField } from 'component/common/form'; type Props = { setSyncEnabled: boolean => void, @@ -13,23 +14,20 @@ type Props = { location: UrlLocation, getSyncError: ?string, disabled: boolean, + openModal: (string, any) => void, }; function SyncToggle(props: Props) { const { - setSyncEnabled, - syncEnabled, verifiedEmail, getSyncError, history, location: { pathname }, - disabled = false, + openModal, + syncEnabled, + disabled, } = props; - function handleChange() { - setSyncEnabled(!syncEnabled); - } - if (getSyncError) { history.push(`/$/${PAGES.AUTH}?redirect=${pathname}&immediate=true`); return null; @@ -48,7 +46,7 @@ function SyncToggle(props: Props) { name="sync_toggle" label={__('Sync your balance and preferences across devices.')} checked={syncEnabled} - onChange={handleChange} + onChange={() => openModal(MODALS.SYNC_ENABLE, { mode: syncEnabled ? 'disable' : 'enable' })} disabled={disabled} /> )} diff --git a/ui/component/userEmailNew/index.js b/ui/component/userEmailNew/index.js index 04e94100b..6cf78a253 100644 --- a/ui/component/userEmailNew/index.js +++ b/ui/component/userEmailNew/index.js @@ -7,8 +7,8 @@ import { selectUser, } from 'redux/selectors/user'; import { DAEMON_SETTINGS, SETTINGS } from 'lbry-redux'; -import { doSetSyncPref, doSetDaemonSetting } from 'redux/actions/settings'; -import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings'; +import { doSetWalletSyncPreference, doSetDaemonSetting } from 'redux/actions/settings'; +import { selectDaemonSettings, makeSelectClientSetting } from 'redux/selectors/settings'; import UserEmailNew from './view'; const select = state => ({ @@ -21,7 +21,7 @@ const select = state => ({ }); const perform = dispatch => ({ - setSync: value => dispatch(doSetSyncPref(value)), + setSync: value => dispatch(doSetWalletSyncPreference(value)), setShareDiagnosticData: shouldShareData => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, shouldShareData)), doSignUp: (email, password) => dispatch(doUserSignUp(email, password)), diff --git a/ui/component/userEmailReturning/index.js b/ui/component/userEmailReturning/index.js index 95b7f3fb9..7dd83a05f 100644 --- a/ui/component/userEmailReturning/index.js +++ b/ui/component/userEmailReturning/index.js @@ -8,7 +8,7 @@ import { selectEmailNewIsPending, } from 'redux/selectors/user'; import { doUserCheckIfEmailExists, doClearEmailEntry } from 'redux/actions/user'; -import { doSetSyncPref } from 'redux/actions/settings'; +import { doSetWalletSyncPreference } from 'redux/actions/settings'; import UserEmailReturning from './view'; const select = state => ({ @@ -23,5 +23,5 @@ const select = state => ({ export default connect(select, { doUserCheckIfEmailExists, doClearEmailEntry, - doSetSyncPref, + doSetWalletSyncPreference, })(UserEmailReturning); diff --git a/ui/component/userEmailReturning/view.jsx b/ui/component/userEmailReturning/view.jsx index 2812f9974..dc3cce2e6 100644 --- a/ui/component/userEmailReturning/view.jsx +++ b/ui/component/userEmailReturning/view.jsx @@ -17,7 +17,7 @@ type Props = { doClearEmailEntry: () => void, doUserSignIn: (string, ?string) => void, doUserCheckIfEmailExists: string => void, - doSetSyncPref: boolean => void, + doSetWalletSyncPreference: boolean => void, doSetClientSetting: (string, boolean, ?boolean) => void, isPending: boolean, }; @@ -30,7 +30,7 @@ function UserEmailReturning(props: Props) { emailToVerify, doClearEmailEntry, emailDoesNotExist, - doSetSyncPref, + doSetWalletSyncPreference, isPending, } = props; const { push, location } = useHistory(); @@ -48,7 +48,7 @@ function UserEmailReturning(props: Props) { function handleSubmit() { // @if TARGET='app' - doSetSyncPref(syncEnabled); + doSetWalletSyncPreference(syncEnabled); // @endif doUserCheckIfEmailExists(email); } diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index 5540ae0e9..d56eed1b0 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -133,7 +133,6 @@ 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'; diff --git a/ui/constants/modal_types.js b/ui/constants/modal_types.js index eea7e63e6..517580aa3 100644 --- a/ui/constants/modal_types.js +++ b/ui/constants/modal_types.js @@ -39,5 +39,6 @@ export const REPOST = 'repost'; export const SIGN_OUT = 'sign_out'; export const LIQUIDATE_SUPPORTS = 'liquidate_supports'; export const CONFIRM_AGE = 'confirm_age'; +export const SYNC_ENABLE = 'SYNC_ENABLE'; export const REMOVE_BLOCKED = 'remove_blocked'; export const IMAGE_UPLOAD = 'image_upload'; diff --git a/ui/modal/modalRouter/view.jsx b/ui/modal/modalRouter/view.jsx index 5c8bd34c0..727f648eb 100644 --- a/ui/modal/modalRouter/view.jsx +++ b/ui/modal/modalRouter/view.jsx @@ -38,6 +38,7 @@ import ModalSignOut from 'modal/modalSignOut'; import ModalSupportsLiquidate from 'modal/modalSupportsLiquidate'; import ModalConfirmAge from 'modal/modalConfirmAge'; import ModalFileSelection from 'modal/modalFileSelection'; +import ModalSyncEnable from 'modal/modalSyncEnable'; import ModalImageUpload from 'modal/modalImageUpload'; type Props = { @@ -140,6 +141,8 @@ function ModalRouter(props: Props) { return ; case MODALS.IMAGE_UPLOAD: return ; + case MODALS.SYNC_ENABLE: + return ; default: return null; } diff --git a/ui/modal/modalSyncEnable/index.js b/ui/modal/modalSyncEnable/index.js new file mode 100644 index 000000000..662df980b --- /dev/null +++ b/ui/modal/modalSyncEnable/index.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux'; +import { doHideModal } from 'redux/actions/app'; +import ModalSyncEnable from './view'; + +const perform = dispatch => () => ({ + closeModal: () => { + dispatch(doHideModal()); + }, +}); + +export default connect(null, perform)(ModalSyncEnable); diff --git a/ui/modal/modalSyncEnable/view.jsx b/ui/modal/modalSyncEnable/view.jsx new file mode 100644 index 000000000..aa31e2f31 --- /dev/null +++ b/ui/modal/modalSyncEnable/view.jsx @@ -0,0 +1,21 @@ +// @flow +import React from 'react'; +import { Modal } from 'modal/modal'; +import SyncToggleFlow from 'component/syncEnableFlow'; + +type Props = { + closeModal: () => void, + mode: string, +}; + +const ModalSyncEnable = (props: Props) => { + const { closeModal, mode } = props; + + return ( + + + + ); +}; + +export default ModalSyncEnable; diff --git a/ui/page/settings/index.js b/ui/page/settings/index.js index 16ee2bbcc..2b2ca57aa 100644 --- a/ui/page/settings/index.js +++ b/ui/page/settings/index.js @@ -33,7 +33,6 @@ const select = state => ({ hideReposts: makeSelectClientSetting(SETTINGS.HIDE_REPOSTS)(state), darkModeTimes: makeSelectClientSetting(SETTINGS.DARK_MODE_TIMES)(state), language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state), - syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state), }); const perform = dispatch => ({ diff --git a/ui/page/settings/view.jsx b/ui/page/settings/view.jsx index 900513b68..2e64e1e15 100644 --- a/ui/page/settings/view.jsx +++ b/ui/page/settings/view.jsx @@ -64,7 +64,6 @@ type Props = { setDarkTime: (string, {}) => void, openModal: string => void, language?: string, - syncEnabled: boolean, enterSettings: () => void, exitSettings: () => void, }; diff --git a/ui/page/settingsAdvanced/view.jsx b/ui/page/settingsAdvanced/view.jsx index a0df40293..67bd2077a 100644 --- a/ui/page/settingsAdvanced/view.jsx +++ b/ui/page/settingsAdvanced/view.jsx @@ -53,7 +53,6 @@ type Props = { findingFFmpeg: boolean, findFFmpeg: () => void, language?: string, - syncEnabled: boolean, syncSettings: () => void, enterSettings: () => void, exitSettings: () => void, diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index 61f3623b1..6b942cb11 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -21,6 +21,7 @@ import { selectFollowedTagsList, SHARED_PREFERENCES, DAEMON_SETTINGS, + SETTINGS, } from 'lbry-redux'; import { doToast, doError, doNotificationList } from 'redux/actions/notifications'; import Native from 'native'; @@ -43,7 +44,7 @@ import { selectModal, selectAllowAnalytics, } from 'redux/selectors/app'; -import { selectDaemonSettings } from 'redux/selectors/settings'; +import { selectDaemonSettings, makeSelectClientSetting } from 'redux/selectors/settings'; import { selectUser } from 'redux/selectors/user'; // import { selectDaemonSettings } from 'redux/selectors/settings'; import { doSyncSubscribe } from 'redux/actions/syncwrapper'; @@ -596,9 +597,11 @@ export function doGetAndPopulatePreferences() { return (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; let preferenceKey; // @if TARGET='app' - preferenceKey = state.user && state.user.user && state.user.user.has_verified_email ? 'shared' : 'anon'; + preferenceKey = syncEnabled && hasVerifiedEmail ? 'shared' : 'local'; // @endif // @if TARGET='web' preferenceKey = 'shared'; diff --git a/ui/redux/actions/settings.js b/ui/redux/actions/settings.js index 271e0aadc..57c37c499 100644 --- a/ui/redux/actions/settings.js +++ b/ui/redux/actions/settings.js @@ -131,34 +131,12 @@ export function doSetClientSetting(key, value, pushPrefs) { value, }, }); - if (pushPrefs) { dispatch(doPushSettingsToPrefs()); } }; } -export function doUpdateSyncPrefIfFalse() { - // This is only called after manual signin to update the wallet - // that the sync preference is false - return (dispatch, getState) => { - const state = getState(); - const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); - if (syncEnabled === false) { - dispatch(doPushSettingsToPrefs()); - } - }; -} - -export function doSetSyncPref(isEnabled) { - return dispatch => { - dispatch({ - type: LOCAL_ACTIONS.SYNC_PREFERENCE_CHANGED, - data: isEnabled, - }); - }; -} - export function doUpdateIsNight() { return { type: ACTIONS.UPDATE_IS_NIGHT, @@ -195,6 +173,32 @@ export function doSetDarkTime(value, options) { }; } +export function doGetWalletSyncPreference() { + const SYNC_KEY = 'enable-sync'; + return dispatch => { + return Lbry.preference_get({ key: SYNC_KEY }).then(result => { + const enabled = result && result[SYNC_KEY]; + if (enabled !== null) { + dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, enabled)); + } + return enabled; + }); + }; +} + +export function doSetWalletSyncPreference(pref) { + const SYNC_KEY = 'enable-sync'; + return dispatch => { + return Lbry.preference_set({ key: SYNC_KEY, value: pref }).then(result => { + const enabled = result && result[SYNC_KEY]; + if (enabled !== null) { + dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, enabled)); + } + return enabled; + }); + }; +} + export function doPushSettingsToPrefs() { return dispatch => { return new Promise((resolve, reject) => { @@ -211,6 +215,9 @@ export function doEnterSettingsPage() { const state = getState(); const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); const hasVerifiedEmail = state.user && state.user.user && state.user.user.has_verified_email; + if (IS_WEB && !hasVerifiedEmail) { + return; + } dispatch(doSyncUnsubscribe()); if (syncEnabled && hasVerifiedEmail) { await dispatch(doGetSyncDesktop()); @@ -223,6 +230,11 @@ export function doEnterSettingsPage() { export function doExitSettingsPage() { return (dispatch, getState) => { + const state = getState(); + const hasVerifiedEmail = state.user && state.user.user && state.user.user.has_verified_email; + if (IS_WEB && !hasVerifiedEmail) { + return; + } dispatch(doSetSyncLock(false)); dispatch(doPushSettingsToPrefs()); // syncSubscribe is restarted in store.js sharedStateCB if necessary diff --git a/ui/redux/actions/syncwrapper.js b/ui/redux/actions/syncwrapper.js index 74b5fa7fe..6a6e4ce7f 100644 --- a/ui/redux/actions/syncwrapper.js +++ b/ui/redux/actions/syncwrapper.js @@ -1,11 +1,11 @@ // @flow import { doGetSync, selectGetSyncIsPending, selectSetSyncIsPending } from 'lbryinc'; -import { SETTINGS } from 'lbry-redux'; -import { makeSelectClientSetting, selectSyncSigninPref } from 'redux/selectors/settings'; +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'; +import { SETTINGS } from 'lbry-redux'; let syncTimer = null; const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes @@ -31,10 +31,9 @@ export function doSyncSubscribe() { if (syncTimer) clearInterval(syncTimer); const state = getState(); const hasVerifiedEmail = selectUserVerifiedEmail(state); - const signInSyncPref = selectSyncSigninPref(state); const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); const syncLocked = selectSyncIsLocked(state); - if (hasVerifiedEmail && syncEnabled && !syncLocked && signInSyncPref !== false) { + if (hasVerifiedEmail && syncEnabled && !syncLocked) { dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData)))); dispatch(doAnalyticsTagSync()); syncTimer = setInterval(() => { diff --git a/ui/redux/reducers/settings.js b/ui/redux/reducers/settings.js index 8ad323cb2..db2b2e4c7 100644 --- a/ui/redux/reducers/settings.js +++ b/ui/redux/reducers/settings.js @@ -24,7 +24,6 @@ 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: {} }, @@ -153,28 +152,15 @@ 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, syncEnabledPref } = state; + const { clientSettings: currentClientSettings } = state; const { settings: sharedPreferences } = action.data; - // we have to update the signin sync checkbox in here - // where we can simulataneously set the checkbox temp value to undefined, and - // update the sync setting before sync happens - // temp box must be undefined to trigger the items in the syncSubscribe effect - const syncSettingOrEmpty = syncEnabledPref !== undefined ? { enable_sync: syncEnabledPref } : {}; - const selectedSettings = sharedPreferences ? getSubsetFromKeysArray(sharedPreferences, clientSyncKeys) : {}; - const mergedClientSettings = { ...currentClientSettings, ...selectedSettings, ...syncSettingOrEmpty }; + const mergedClientSettings = { ...currentClientSettings, ...selectedSettings }; const newSharedPreferences = sharedPreferences || {}; return Object.assign({}, state, { sharedPreferences: newSharedPreferences, clientSettings: mergedClientSettings, - syncEnabledPref: undefined, }); }; diff --git a/ui/redux/selectors/settings.js b/ui/redux/selectors/settings.js index e137a5b80..8e7f3580f 100644 --- a/ui/redux/selectors/settings.js +++ b/ui/redux/selectors/settings.js @@ -11,8 +11,6 @@ export const selectFfmpegStatus = createSelector(selectDaemonStatus, status => s export const selectFindingFFmpeg = createSelector(selectState, state => state.findingFFmpeg || false); -export const selectSyncSigninPref = createSelector(selectState, state => state.syncEnabledPref); - export const selectClientSettings = createSelector(selectState, state => state.clientSettings || {}); export const selectLoadedLanguages = createSelector(selectState, state => state.loadedLanguages || {}); diff --git a/yarn.lock b/yarn.lock index d41b61511..b8252a4a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6411,9 +6411,9 @@ lazy-val@^1.0.4: yargs "^13.2.2" zstd-codec "^0.1.1" -lbry-redux@lbryio/lbry-redux#437c54f164b4132c0d5c6ede60c7b02703f1e9d5: +lbry-redux@lbryio/lbry-redux#e10295b5ec2ef42426755ed9fd50bd250b0d76dd: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/437c54f164b4132c0d5c6ede60c7b02703f1e9d5" + resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/e10295b5ec2ef42426755ed9fd50bd250b0d76dd" dependencies: proxy-polyfill "0.1.6" reselect "^3.0.0"