don't lose subs/tags for existing users and don't opt them into sync automatically
This commit is contained in:
parent
ff702e480a
commit
4e70642163
11 changed files with 106 additions and 43 deletions
|
@ -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",
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -68,7 +68,6 @@ const whiteListedReducers = [
|
|||
'search',
|
||||
'blocked',
|
||||
'settings',
|
||||
'sync',
|
||||
'subscriptions',
|
||||
];
|
||||
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue