don't lose subs/tags for existing users and don't opt them into sync automatically

This commit is contained in:
Sean Yesmunt 2019-10-17 11:27:41 -04:00
parent ff702e480a
commit 4e70642163
11 changed files with 106 additions and 43 deletions

View file

@ -89,7 +89,6 @@
"codemirror": "^5.39.2",
"concurrently": "^4.1.2",
"connected-react-router": "^6.4.0",
"cookie": "^0.3.1",
"copy-webpack-plugin": "^4.6.0",
"country-data": "^0.0.31",
"cross-env": "^5.2.0",

View file

@ -1,7 +1,7 @@
import * as SETTINGS from 'constants/settings';
import { hot } from 'react-hot-loader/root';
import { connect } from 'react-redux';
import { selectUser, doRewardList, doFetchRewardedContent, doFetchAccessToken } from 'lbryinc';
import { selectUser, doRewardList, doFetchRewardedContent, doFetchAccessToken, selectAccessToken } from 'lbryinc';
import { doFetchTransactions, doFetchChannelListMine, selectBalance } from 'lbry-redux';
import { makeSelectClientSetting, selectThemePath } from 'redux/selectors/settings';
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
@ -17,6 +17,7 @@ const select = state => ({
isUpgradeAvailable: selectIsUpgradeAvailable(state),
balance: selectBalance(state),
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
accessToken: selectAccessToken(state),
});
const perform = dispatch => ({

View file

@ -1,5 +1,6 @@
// @flow
import * as ICONS from 'constants/icons';
import * as ACTIONS from 'constants/action_types';
import React, { useEffect, useRef, useState } from 'react';
import classnames from 'classnames';
import analytics from 'analytics';
@ -14,7 +15,8 @@ import FileViewer from 'component/fileViewer';
import { withRouter } from 'react-router';
import usePrevious from 'effects/use-previous';
import Button from 'component/button';
import cookie from 'cookie';
import usePersistedState from 'effects/use-persisted-state';
import { Lbryio } from 'lbryinc';
export const MAIN_WRAPPER_CLASS = 'main-wrapper';
// @if TARGET='app'
@ -22,8 +24,6 @@ export const IS_MAC = process.platform === 'darwin';
// @endif
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
const { auth_token: authToken } = cookie.parse(document.cookie);
type Props = {
alertError: (string | {}) => void,
pageTitle: ?string,
@ -45,6 +45,8 @@ type Props = {
checkSync: () => void,
setSyncEnabled: boolean => void,
syncEnabled: boolean,
balance: ?number,
accessToken: ?string,
};
function App(props: Props) {
@ -63,11 +65,14 @@ function App(props: Props) {
setSyncEnabled,
syncEnabled,
checkSync,
balance,
accessToken,
} = props;
const appRef = useRef();
const isEnhancedLayout = useKonamiListener();
const [hasSignedIn, setHasSignedIn] = useState(false);
const [hasDeterminedIfNewUser, setHasDeterminedIfNewUser] = usePersistedState('is-new-user', false);
const userId = user && user.id;
const hasVerifiedEmail = user && user.has_verified_email;
const isRewardApproved = user && user.is_reward_approved;
@ -88,11 +93,35 @@ function App(props: Props) {
// to automatically opt-in existing users. Only users that go through the new sign in flow
// should be automatically opted-in (they choose to uncheck the option and turn off sync still)
useEffect(() => {
if (!authToken) {
setSyncEnabled(true);
if (balance === undefined || accessToken === undefined) {
return;
}
// don't pass in any props to this, we only want the initial value
}, []);
// Manually call subscription/list once because I was dumb and wasn't persisting it in redux
Lbryio.call('subscription', 'list').then(response => {
if (response && response.length) {
const subscriptions = response.map(value => {
const { channel_name: channelName, claim_id: claimId } = value;
return {
channelName,
uri: buildURI({ channelName, channelClaimId: claimId }),
};
});
window.store.dispatch({
type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS,
data: subscriptions,
});
}
// Yeah... this isn't the best check, but it works for now
const newUser = balance === 0;
if (newUser) {
setSyncEnabled(true);
}
setHasDeterminedIfNewUser(true);
});
}, [balance, accessToken]);
useEffect(() => {
ReactModal.setAppElement(appRef.current);
@ -141,7 +170,7 @@ function App(props: Props) {
}, [hasVerifiedEmail, signIn, hasSignedIn]);
useEffect(() => {
if (hasVerifiedEmail && syncEnabled) {
if (hasVerifiedEmail && syncEnabled && hasDeterminedIfNewUser) {
checkSync();
let syncInterval = setInterval(() => {
@ -152,7 +181,7 @@ function App(props: Props) {
clearInterval(syncInterval);
};
}
}, [hasVerifiedEmail, syncEnabled, checkSync]);
}, [hasVerifiedEmail, syncEnabled, checkSync, hasDeterminedIfNewUser]);
if (!user) {
return null;

View file

@ -26,10 +26,10 @@ import pjson from 'package.json';
import app from './app';
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
import { ConnectedRouter, push } from 'connected-react-router';
import cookie from 'cookie';
import { formatLbryUriForWeb } from 'util/uri';
import { PersistGate } from 'redux-persist/integration/react';
import analytics from 'analytics';
import { getAuthToken, setAuthToken } from 'util/saved-passwords';
// Import our app styles
// If a style is not necessary for the initial page load, it should be removed from `all.scss`
@ -82,12 +82,7 @@ Lbryio.setOverride(
}
authToken = response.auth_token;
let date = new Date();
date.setFullYear(date.getFullYear() + 1);
document.cookie = cookie.serialize('auth_token', authToken, {
expires: date,
});
setAuthToken(authToken);
// @if TARGET='app'
ipcRenderer.send('set-auth-token', authToken);
@ -114,7 +109,7 @@ Lbryio.setOverride(
ipcRenderer.send('get-auth-token');
// @endif
// @if TARGET='web'
const { auth_token: authToken } = cookie.parse(document.cookie);
const authToken = getAuthToken();
resolve(authToken);
// @endif
}

View file

@ -74,6 +74,10 @@ class ModalWalletEncrypt extends React.PureComponent<Props, State> {
submitEncryptForm() {
const { state } = this;
if (!state.newPassword) {
return;
}
let invalidEntries = false;
if (state.newPassword !== state.newPasswordConfirm) {
@ -89,9 +93,8 @@ class ModalWalletEncrypt extends React.PureComponent<Props, State> {
if (invalidEntries === true) {
return;
}
if (state.rememberPassword === true) {
setSavedPassword(state.newPassword);
}
setSavedPassword(state.newPassword, state.rememberPassword);
this.setState({ submitted: true });
this.props.encryptWallet(state.newPassword);
}

View file

@ -39,9 +39,7 @@ class ModalWalletUnlock extends React.PureComponent<Props, State> {
const { props } = this;
if (props.walletUnlockSucceded === true) {
if (this.state.rememberPassword) {
setSavedPassword(this.state.password);
}
setSavedPassword(this.state.password, this.state.rememberPassword);
props.closeModal();
}
}

View file

@ -37,8 +37,7 @@ import { doAuthenticate, doGetSync, doResetSync } from 'lbryinc';
import { lbrySettings as config, version as appVersion } from 'package.json';
import { push } from 'connected-react-router';
import analytics from 'analytics';
import { deleteAuthToken, getSavedPassword } from 'util/saved-passwords';
import cookie from 'cookie';
import { deleteAuthToken, getSavedPassword, getAuthToken } from 'util/saved-passwords';
// @if TARGET='app'
const { autoUpdater } = remote.require('electron-updater');
@ -437,7 +436,7 @@ export function doAnalyticsView(uri, timeToStart) {
export function doSignIn() {
return (dispatch, getState) => {
// @if TARGET='web'
const { auth_token: authToken } = cookie.parse(document.cookie);
const authToken = getAuthToken();
Lbry.setApiHeader('X-Lbry-Auth-Token', authToken);
dispatch(doBalanceSubscribe());
dispatch(doFetchChannelListMine());
@ -471,7 +470,9 @@ export function doSyncWithPreferences() {
dispatch(doFetchChannelListMine());
function successCb(savedPreferences) {
dispatch(doPopulateSharedUserState(savedPreferences));
if (savedPreferences !== null) {
dispatch(doPopulateSharedUserState(savedPreferences));
}
}
function failCb() {

View file

@ -68,7 +68,6 @@ const whiteListedReducers = [
'search',
'blocked',
'settings',
'sync',
'subscriptions',
];

View file

@ -1,9 +1,42 @@
// @flow
import { ipcRenderer } from 'electron';
let sessionPassword;
export const setSavedPassword = (value, saveToDisk) => {
return new Promise(resolve => {
function setCookie(name: string, value: string, days: number) {
let expires = '';
if (days) {
let date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = '; expires=' + date.toUTCString();
}
document.cookie = `${name}=${value || ''}${expires}; path=/`;
}
function getCookie(name: string) {
const nameEQ = name + '=';
const cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
let cookie = cookies[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1, cookie.length);
}
if (cookie.indexOf(nameEQ) === 0) {
return cookie.substring(nameEQ.length, cookie.length);
}
}
return null;
}
function deleteCookie(name: string) {
document.cookie = name + '=; Max-Age=-99999999;';
}
export const setSavedPassword = (value?: string, saveToDisk: boolean) => {
return new Promise<*>(resolve => {
ipcRenderer.once('set-password-response', (event, success) => {
resolve(success);
});
@ -16,7 +49,7 @@ export const setSavedPassword = (value, saveToDisk) => {
};
export const getSavedPassword = () => {
return new Promise(resolve => {
return new Promise<*>(resolve => {
if (sessionPassword) {
resolve(sessionPassword);
}
@ -36,7 +69,7 @@ export const getSavedPassword = () => {
};
export const deleteSavedPassword = () => {
return new Promise(resolve => {
return new Promise<*>(resolve => {
// @if TARGET='app'
ipcRenderer.once('delete-password-response', (event, success) => {
resolve();
@ -46,16 +79,25 @@ export const deleteSavedPassword = () => {
});
};
export const getAuthToken = () => {
return getCookie('auth_token');
};
export const setAuthToken = (value: string) => {
return setCookie('auth_token', value, 365);
};
export const deleteAuthToken = () => {
return new Promise(resolve => {
return new Promise<*>(resolve => {
// @if TARGET='app'
ipcRenderer.once('delete-auth-token-response', (event, success) => {
resolve();
});
ipcRenderer.send('delete-auth-token');
// @endif;
deleteCookie('auth_token');
// @if TARGET='web'
document.cookie = 'auth_token= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
resolve();
// @endif
});

View file

@ -820,5 +820,6 @@
"Content may be hidden on this %type% because of your %settings%.": "Content may be hidden on this %type% because of your %settings%.",
"Sync": "Sync",
"earned and bound in tips": "earned and bound in tips",
"currently staked": "currently staked"
}
"currently staked": "currently staked",
"%amountBehind% block behind": "%amountBehind% block behind"
}

View file

@ -2941,11 +2941,6 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookie@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
copy-concurrently@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"