From 3b23f09bed2bb790faccc6d527abbdd8726e9363 Mon Sep 17 00:00:00 2001 From: jessop Date: Fri, 11 Sep 2020 14:35:50 -0400 Subject: [PATCH] fix sync clean wallet bug remove previous changes,keep syncpref in wallet, change anon wallet pref key to local sync choices wip dont relocate syncenable setting bump no prefs on web unauth bugfix redux bump pull after sync change bump --- package.json | 2 +- static/app-strings.json | 15 ++ ui/component/app/index.js | 12 +- ui/component/app/view.jsx | 38 ++-- ui/component/syncEnableFlow/index.js | 34 ++++ ui/component/syncEnableFlow/view.jsx | 234 +++++++++++++++++++++++ ui/component/syncToggle/index.js | 6 +- ui/component/syncToggle/view.jsx | 16 +- ui/component/userEmailNew/index.js | 6 +- ui/component/userEmailReturning/index.js | 4 +- ui/component/userEmailReturning/view.jsx | 6 +- ui/constants/action_types.js | 1 - ui/constants/modal_types.js | 1 + ui/modal/modalRouter/view.jsx | 3 + ui/modal/modalSyncEnable/index.js | 11 ++ ui/modal/modalSyncEnable/view.jsx | 21 ++ ui/page/settings/index.js | 1 - ui/page/settings/view.jsx | 1 - ui/page/settingsAdvanced/view.jsx | 1 - ui/redux/actions/app.js | 7 +- ui/redux/actions/settings.js | 56 +++--- ui/redux/actions/syncwrapper.js | 7 +- ui/redux/reducers/settings.js | 18 +- ui/redux/selectors/settings.js | 2 - yarn.lock | 4 +- 25 files changed, 402 insertions(+), 105 deletions(-) create mode 100644 ui/component/syncEnableFlow/index.js create mode 100644 ui/component/syncEnableFlow/view.jsx create mode 100644 ui/modal/modalSyncEnable/index.js create mode 100644 ui/modal/modalSyncEnable/view.jsx 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"