From c0e9b92a3ae486b853600f3729e3b124c07db60f Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Wed, 10 Jan 2018 21:41:51 -0800 Subject: [PATCH 01/22] Added phone verification option --- src/renderer/component/userVerify/view.jsx | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/renderer/component/userVerify/view.jsx b/src/renderer/component/userVerify/view.jsx index 07cadba99..424682013 100644 --- a/src/renderer/component/userVerify/view.jsx +++ b/src/renderer/component/userVerify/view.jsx @@ -66,7 +66,29 @@ class UserVerify extends React.PureComponent {
-

{__('2) Proof via YouTube')}

+

{__('2) Proof via Phone')}

+
+
+ {`${__( + 'You will receive an SMS text message confirming that your phone number is correct.' + )}`} +
+
+ {errorMessage &&

{errorMessage}

} + +
+
+
{__('Standard messaging rates apply.')}
+
+
+
+
+

{__('3) Proof via YouTube')}

@@ -96,7 +118,7 @@ class UserVerify extends React.PureComponent {

-

{__('3) Proof via Chat')}

+

{__('4) Proof via Chat')}

From c52c75758bafaaf9bf80a5ef5719884bfdb2b009 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Wed, 10 Jan 2018 22:00:51 -0800 Subject: [PATCH 02/22] Rename modalEmailCollection to modalPhoneCollection --- .../{modalEmailCollection => modalPhoneCollection}/index.js | 4 ++-- .../{modalEmailCollection => modalPhoneCollection}/view.jsx | 4 ++-- src/renderer/modal/modalRouter/view.jsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/renderer/modal/{modalEmailCollection => modalPhoneCollection}/index.js (84%) rename src/renderer/modal/{modalEmailCollection => modalPhoneCollection}/view.jsx (91%) diff --git a/src/renderer/modal/modalEmailCollection/index.js b/src/renderer/modal/modalPhoneCollection/index.js similarity index 84% rename from src/renderer/modal/modalEmailCollection/index.js rename to src/renderer/modal/modalPhoneCollection/index.js index 242b9e509..aae6952f7 100644 --- a/src/renderer/modal/modalEmailCollection/index.js +++ b/src/renderer/modal/modalPhoneCollection/index.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { doCloseModal } from 'redux/actions/app'; import { doSetClientSetting } from 'redux/actions/settings'; import { selectEmailToVerify, selectUser } from 'redux/selectors/user'; -import ModalEmailCollection from './view'; +import ModalPhoneCollection from './view'; const select = state => ({ email: selectEmailToVerify(state), @@ -18,4 +18,4 @@ const perform = dispatch => () => ({ }, }); -export default connect(select, perform)(ModalEmailCollection); +export default connect(select, perform)(ModalPhoneCollection); diff --git a/src/renderer/modal/modalEmailCollection/view.jsx b/src/renderer/modal/modalPhoneCollection/view.jsx similarity index 91% rename from src/renderer/modal/modalEmailCollection/view.jsx rename to src/renderer/modal/modalPhoneCollection/view.jsx index a17ecf7df..16b8cbe08 100644 --- a/src/renderer/modal/modalEmailCollection/view.jsx +++ b/src/renderer/modal/modalPhoneCollection/view.jsx @@ -4,7 +4,7 @@ import Link from 'component/link/index'; import UserEmailNew from 'component/userEmailNew'; import UserEmailVerify from 'component/userEmailVerify'; -class ModalEmailCollection extends React.PureComponent { +class ModalPhoneCollection extends React.PureComponent { renderInner() { const { closeModal, email, user } = this.props; @@ -37,4 +37,4 @@ class ModalEmailCollection extends React.PureComponent { } } -export default ModalEmailCollection; +export default ModalPhoneCollection; diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx index b2d68fbf2..019a799fe 100644 --- a/src/renderer/modal/modalRouter/view.jsx +++ b/src/renderer/modal/modalRouter/view.jsx @@ -12,7 +12,7 @@ import ModalTransactionFailed from 'modal/modalTransactionFailed'; import ModalFileTimeout from 'modal/modalFileTimeout'; import ModalAffirmPurchase from 'modal/modalAffirmPurchase'; import ModalRevokeClaim from 'modal/modalRevokeClaim'; -import ModalEmailCollection from '../modalEmailCollection'; +import ModalPhoneCollection from '../modalPhoneCollection'; import * as modals from 'constants/modal_types'; class ModalRouter extends React.PureComponent { @@ -125,7 +125,7 @@ class ModalRouter extends React.PureComponent { case modals.CONFIRM_CLAIM_REVOKE: return ; case modals.EMAIL_COLLECTION: - return ; + return ; default: return null; } From b9b7af2bbd543f4bfd98a4dec1f2a821cdcb85eb Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Wed, 10 Jan 2018 22:16:03 -0800 Subject: [PATCH 03/22] Renamed userEmail... to userField... --- .../{userEmailNew => userFieldNew}/index.js | 8 ++++---- .../{userEmailNew => userFieldNew}/view.jsx | 4 ++-- .../{userEmailVerify => userFieldVerify}/index.js | 10 +++++----- .../{userEmailVerify => userFieldVerify}/view.jsx | 4 ++-- src/renderer/index.js | 4 ++-- src/renderer/modal/modalPhoneCollection/view.jsx | 12 ++++++------ src/renderer/page/auth/view.jsx | 8 ++++---- src/renderer/redux/actions/user.js | 8 ++++---- 8 files changed, 29 insertions(+), 29 deletions(-) rename src/renderer/component/{userEmailNew => userFieldNew}/index.js (60%) rename src/renderer/component/{userEmailNew => userFieldNew}/view.jsx (94%) rename src/renderer/component/{userEmailVerify => userFieldVerify}/index.js (61%) rename src/renderer/component/{userEmailVerify => userFieldVerify}/view.jsx (95%) diff --git a/src/renderer/component/userEmailNew/index.js b/src/renderer/component/userFieldNew/index.js similarity index 60% rename from src/renderer/component/userEmailNew/index.js rename to src/renderer/component/userFieldNew/index.js index 0228a4aaa..78c7810be 100644 --- a/src/renderer/component/userEmailNew/index.js +++ b/src/renderer/component/userFieldNew/index.js @@ -1,8 +1,8 @@ import React from 'react'; import { connect } from 'react-redux'; -import { doUserEmailNew, doUserInviteNew } from 'redux/actions/user'; +import { doUserFieldNew, doUserInviteNew } from 'redux/actions/user'; import { selectEmailNewIsPending, selectEmailNewErrorMessage } from 'redux/selectors/user'; -import UserEmailNew from './view'; +import UserFieldNew from './view'; const select = state => ({ isPending: selectEmailNewIsPending(state), @@ -10,7 +10,7 @@ const select = state => ({ }); const perform = dispatch => ({ - addUserEmail: email => dispatch(doUserEmailNew(email)), + addUserEmail: email => dispatch(doUserFieldNew(email)), }); -export default connect(select, perform)(UserEmailNew); +export default connect(select, perform)(UserFieldNew); diff --git a/src/renderer/component/userEmailNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx similarity index 94% rename from src/renderer/component/userEmailNew/view.jsx rename to src/renderer/component/userFieldNew/view.jsx index 661adb611..2dd0fffd6 100644 --- a/src/renderer/component/userEmailNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { Form, FormRow, Submit } from 'component/form.js'; -class UserEmailNew extends React.PureComponent { +class UserFieldNew extends React.PureComponent { constructor(props) { super(props); @@ -52,4 +52,4 @@ class UserEmailNew extends React.PureComponent { } } -export default UserEmailNew; +export default UserFieldNew; diff --git a/src/renderer/component/userEmailVerify/index.js b/src/renderer/component/userFieldVerify/index.js similarity index 61% rename from src/renderer/component/userEmailVerify/index.js rename to src/renderer/component/userFieldVerify/index.js index f8c854ca1..677df5b25 100644 --- a/src/renderer/component/userEmailVerify/index.js +++ b/src/renderer/component/userFieldVerify/index.js @@ -1,12 +1,12 @@ import React from 'react'; import { connect } from 'react-redux'; -import { doUserEmailVerify, doUserEmailVerifyFailure } from 'redux/actions/user'; +import { doUserFieldVerify, doUserFieldVerifyFailure } from 'redux/actions/user'; import { selectEmailVerifyIsPending, selectEmailToVerify, selectEmailVerifyErrorMessage, } from 'redux/selectors/user'; -import UserEmailVerify from './view'; +import UserFieldVerify from './view'; const select = state => ({ isPending: selectEmailVerifyIsPending(state), @@ -15,8 +15,8 @@ const select = state => ({ }); const perform = dispatch => ({ - verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)), - verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)), + verifyUserEmail: (code, recaptcha) => dispatch(doUserFieldVerify(code, recaptcha)), + verifyUserEmailFailure: error => dispatch(doUserFieldVerifyFailure(error)), }); -export default connect(select, perform)(UserEmailVerify); +export default connect(select, perform)(UserFieldVerify); diff --git a/src/renderer/component/userEmailVerify/view.jsx b/src/renderer/component/userFieldVerify/view.jsx similarity index 95% rename from src/renderer/component/userEmailVerify/view.jsx rename to src/renderer/component/userFieldVerify/view.jsx index 3304730b9..450571b8e 100644 --- a/src/renderer/component/userEmailVerify/view.jsx +++ b/src/renderer/component/userFieldVerify/view.jsx @@ -2,7 +2,7 @@ import React from 'react'; import Link from 'component/link'; import { Form, FormRow, Submit } from 'component/form.js'; -class UserEmailVerify extends React.PureComponent { +class UserFieldVerify extends React.PureComponent { constructor(props) { super(props); @@ -59,4 +59,4 @@ class UserEmailVerify extends React.PureComponent { } } -export default UserEmailVerify; +export default UserFieldVerify; diff --git a/src/renderer/index.js b/src/renderer/index.js index fd6dcc1f7..8d92c6ba2 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -12,7 +12,7 @@ import { Provider } from 'react-redux'; import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar } from 'redux/actions/app'; import { doNavigate } from 'redux/actions/navigation'; import { doDownloadLanguages } from 'redux/actions/settings'; -import { doUserEmailVerify } from 'redux/actions/user'; +import { doUserFieldVerify } from 'redux/actions/user'; import 'scss/all.scss'; import store from 'store'; import app from './app'; @@ -35,7 +35,7 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => { } if (verification.token && verification.recaptcha) { app.store.dispatch(doConditionalAuthNavigate(newSession)); - app.store.dispatch(doUserEmailVerify(verification.token, verification.recaptcha)); + app.store.dispatch(doUserFieldVerify(verification.token, verification.recaptcha)); } else { app.store.dispatch(doShowSnackBar({ message: 'Invalid Verification URI' })); } diff --git a/src/renderer/modal/modalPhoneCollection/view.jsx b/src/renderer/modal/modalPhoneCollection/view.jsx index 16b8cbe08..a8b56c844 100644 --- a/src/renderer/modal/modalPhoneCollection/view.jsx +++ b/src/renderer/modal/modalPhoneCollection/view.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { Modal } from 'modal/modal'; import Link from 'component/link/index'; -import UserEmailNew from 'component/userEmailNew'; -import UserEmailVerify from 'component/userEmailVerify'; +import UserFieldNew from 'component/userFieldNew'; +import UserFieldVerify from 'component/userFieldVerify'; class ModalPhoneCollection extends React.PureComponent { renderInner() { @@ -11,9 +11,9 @@ class ModalPhoneCollection extends React.PureComponent { const cancelButton = ; if (!user.has_verified_email && !email) { - return ; + return ; } else if (!user.has_verified_email) { - return ; + return ; } closeModal(); } @@ -27,9 +27,9 @@ class ModalPhoneCollection extends React.PureComponent { } return ( - +

-

Can We Stay In Touch?

+

Verify Your Phone

{this.renderInner()}
diff --git a/src/renderer/page/auth/view.jsx b/src/renderer/page/auth/view.jsx index 423ce8597..908431e26 100644 --- a/src/renderer/page/auth/view.jsx +++ b/src/renderer/page/auth/view.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { BusyMessage } from 'component/common'; import Link from 'component/link'; -import UserEmailNew from 'component/userEmailNew'; -import UserEmailVerify from 'component/userEmailVerify'; +import UserFieldNew from 'component/userFieldNew'; +import UserFieldVerify from 'component/userFieldVerify'; import UserVerify from 'component/userVerify'; export class AuthPage extends React.PureComponent { @@ -45,9 +45,9 @@ export class AuthPage extends React.PureComponent { if (isPending) { return [, true]; } else if (user && !user.has_verified_email && !email) { - return [, true]; + return [, true]; } else if (user && !user.has_verified_email) { - return [, true]; + return [, true]; } else if (user && !user.is_identity_verified) { return [, false]; } diff --git a/src/renderer/redux/actions/user.js b/src/renderer/redux/actions/user.js index 785a45a09..0c289e2ca 100644 --- a/src/renderer/redux/actions/user.js +++ b/src/renderer/redux/actions/user.js @@ -78,7 +78,7 @@ export function doUserFetch() { }; } -export function doUserEmailNew(email) { +export function doUserFieldNew(email) { return dispatch => { dispatch({ type: ACTIONS.USER_EMAIL_NEW_STARTED, @@ -116,14 +116,14 @@ export function doUserEmailNew(email) { }; } -export function doUserEmailVerifyFailure(error) { +export function doUserFieldVerifyFailure(error) { return { type: ACTIONS.USER_EMAIL_VERIFY_FAILURE, data: { error }, }; } -export function doUserEmailVerify(verificationToken, recaptcha) { +export function doUserFieldVerify(verificationToken, recaptcha) { return (dispatch, getState) => { const email = selectEmailToVerify(getState()); @@ -154,7 +154,7 @@ export function doUserEmailVerify(verificationToken, recaptcha) { throw new Error('Your email is still not verified.'); // shouldn't happen } }) - .catch(error => dispatch(doUserEmailVerifyFailure(error))); + .catch(error => dispatch(doUserFieldVerifyFailure(error))); }; } From 8eb7f589889285defeb11dd4889704a9ca6186fd Mon Sep 17 00:00:00 2001 From: Arrowana Date: Sun, 14 Jan 2018 20:14:15 +1100 Subject: [PATCH 04/22] add basic feature --- package.json | 1 + src/renderer/constants/action_types.js | 1 + src/renderer/constants/settings.js | 1 + src/renderer/index.js | 2 ++ src/renderer/page/settings/index.js | 1 + src/renderer/page/settings/view.jsx | 12 +++++++++++ src/renderer/redux/actions/settings.js | 26 ++++++++++++++++++++++++ src/renderer/redux/reducers/settings.js | 7 +++++++ src/renderer/redux/selectors/settings.js | 18 +++++++++++++--- 9 files changed, 66 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index d0dc3f9c7..0ef710ae9 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "jshashes": "^1.0.7", "keytar": "^4.0.3", "localforage": "^1.5.0", + "moment": "^2.20.1", "npm": "^5.5.1", "qrcode.react": "^0.7.2", "rc-progress": "^2.0.6", diff --git a/src/renderer/constants/action_types.js b/src/renderer/constants/action_types.js index 3c828f5cb..861957d36 100644 --- a/src/renderer/constants/action_types.js +++ b/src/renderer/constants/action_types.js @@ -95,6 +95,7 @@ export const SEARCH_CANCELLED = 'SEARCH_CANCELLED'; // Settings export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED'; export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED'; +export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT'; // User export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED'; diff --git a/src/renderer/constants/settings.js b/src/renderer/constants/settings.js index dcb90ab07..9d0040412 100644 --- a/src/renderer/constants/settings.js +++ b/src/renderer/constants/settings.js @@ -11,3 +11,4 @@ export const INSTANT_PURCHASE_ENABLED = 'instantPurchaseEnabled'; export const INSTANT_PURCHASE_MAX = 'instantPurchaseMax'; export const THEME = 'theme'; export const THEMES = 'themes'; +export const AUTOMATIC_DARK_MODE_ENABLED = 'automaticDarkModeEnabled'; diff --git a/src/renderer/index.js b/src/renderer/index.js index fd6dcc1f7..1ef440174 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -10,6 +10,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar } from 'redux/actions/app'; +import { doUpdateIsNightAsync } from 'redux/actions/settings'; import { doNavigate } from 'redux/actions/navigation'; import { doDownloadLanguages } from 'redux/actions/settings'; import { doUserEmailVerify } from 'redux/actions/user'; @@ -97,6 +98,7 @@ document.addEventListener('click', event => { }); const init = () => { + app.store.dispatch(doUpdateIsNightAsync()); app.store.dispatch(doDownloadLanguages()); function onDaemonReady() { diff --git a/src/renderer/page/settings/index.js b/src/renderer/page/settings/index.js index 29d2b325c..ca277df82 100644 --- a/src/renderer/page/settings/index.js +++ b/src/renderer/page/settings/index.js @@ -28,6 +28,7 @@ const select = state => ({ themes: makeSelectClientSetting(settings.THEMES)(state), language: selectCurrentLanguage(state), languages: selectLanguages(state), + automaticDarkModeEnabled: makeSelectClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED)(state), }); const perform = dispatch => ({ diff --git a/src/renderer/page/settings/view.jsx b/src/renderer/page/settings/view.jsx index 67471841c..f3de1c2b3 100644 --- a/src/renderer/page/settings/view.jsx +++ b/src/renderer/page/settings/view.jsx @@ -65,6 +65,10 @@ class SettingsPage extends React.PureComponent { this.props.setClientSetting(settings.THEME, value); } + onAutomaticDarkModeChange(event) { + this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, event.target.checked); + } + onInstantPurchaseEnabledChange(enabled) { this.props.setClientSetting(settings.INSTANT_PURCHASE_ENABLED, enabled); } @@ -129,6 +133,7 @@ class SettingsPage extends React.PureComponent { showUnavailable, theme, themes, + automaticDarkModeEnabled, } = this.props; if (!daemonSettings || Object.keys(daemonSettings).length === 0) { @@ -317,6 +322,13 @@ class SettingsPage extends React.PureComponent { ))} + +
diff --git a/src/renderer/redux/actions/settings.js b/src/renderer/redux/actions/settings.js index 7f0209132..841f18f82 100644 --- a/src/renderer/redux/actions/settings.js +++ b/src/renderer/redux/actions/settings.js @@ -4,6 +4,9 @@ import Fs from 'fs'; import Http from 'http'; import Lbry from 'lbry'; +import moment from 'moment'; + +const UPDATE_IS_NIGHT_INTERVAL = 10 * 1000; export function doFetchDaemonSettings() { return dispatch => { @@ -51,6 +54,29 @@ export function doGetThemes() { }; } +export function doUpdateIsNightAsync() { + return dispatch => { + const updateIsNightInterval = setInterval( + () => dispatch(doUpdateIsNight()), + UPDATE_IS_NIGHT_INTERVAL + ); + }; +} + +function isNight() { + const startNightMoment = moment('19:00', 'HH:mm'); + const endNightTime = moment('8:00', 'HH:mm'); + const momentNow = moment(); + return momentNow.isAfter(endNightTime) && momentNow.isBefore(startNightMoment) ? false : true; +} + +export function doUpdateIsNight() { + return { + type: ACTIONS.UPDATE_IS_NIGHT, + data: { isNight: isNight() }, + }; +} + export function doDownloadLanguage(langFile) { return dispatch => { const destinationPath = `${app.i18n.directory}/${langFile}`; diff --git a/src/renderer/redux/reducers/settings.js b/src/renderer/redux/reducers/settings.js index 914404bb1..a3e6e0536 100644 --- a/src/renderer/redux/reducers/settings.js +++ b/src/renderer/redux/reducers/settings.js @@ -23,7 +23,9 @@ const defaultState = { language: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'), theme: getLocalStorageSetting(SETTINGS.THEME, 'light'), themes: getLocalStorageSetting(SETTINGS.THEMES, []), + automaticDarkModeEnabled: getLocalStorageSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED, false), }, + isNight: true, languages: {}, }; @@ -46,6 +48,11 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => { }); }; +reducers[ACTIONS.UPDATE_IS_NIGHT] = (state, action) => + Object.assign({}, state, { + isNight: action.data.isNight, + }); + reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED] = (state, action) => { const languages = Object.assign({}, state.languages); const { language } = action.data; diff --git a/src/renderer/redux/selectors/settings.js b/src/renderer/redux/selectors/settings.js index 0e2887f59..242fe4b7a 100644 --- a/src/renderer/redux/selectors/settings.js +++ b/src/renderer/redux/selectors/settings.js @@ -1,5 +1,6 @@ import * as SETTINGS from 'constants/settings'; import { createSelector } from 'reselect'; +import moment from 'moment'; const selectState = state => state.settings || {}; @@ -18,7 +19,18 @@ export const selectShowNsfw = makeSelectClientSetting(SETTINGS.SHOW_NSFW); export const selectLanguages = createSelector(selectState, state => state.languages || {}); -export const selectThemePath = createSelector( - makeSelectClientSetting(SETTINGS.THEME), - theme => `${staticResourcesPath}/themes/${theme || 'light'}.css` +export const selectTheme = makeSelectClientSetting(SETTINGS.THEME); +export const selectAutomaticDarkModeEnabled = makeSelectClientSetting( + SETTINGS.AUTOMATIC_DARK_MODE_ENABLED +); +export const selectIsNight = createSelector(selectState, state => state.isNight); + +export const selectThemePath = createSelector( + selectTheme, + selectAutomaticDarkModeEnabled, + selectIsNight, + (theme, automaticDarkModeEnabled, isNight) => { + const dynamicTheme = automaticDarkModeEnabled && isNight ? 'dark' : theme; + return `${staticResourcesPath}/themes/${dynamicTheme || 'light'}.css`; + } ); From 4795901e125d5aceeec581b74b966590457a5f80 Mon Sep 17 00:00:00 2001 From: Arrowana Date: Mon, 15 Jan 2018 20:09:40 +1100 Subject: [PATCH 05/22] improve and clean --- src/renderer/redux/actions/settings.js | 2 +- src/renderer/redux/selectors/settings.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/renderer/redux/actions/settings.js b/src/renderer/redux/actions/settings.js index 841f18f82..2e7dd46b3 100644 --- a/src/renderer/redux/actions/settings.js +++ b/src/renderer/redux/actions/settings.js @@ -67,7 +67,7 @@ function isNight() { const startNightMoment = moment('19:00', 'HH:mm'); const endNightTime = moment('8:00', 'HH:mm'); const momentNow = moment(); - return momentNow.isAfter(endNightTime) && momentNow.isBefore(startNightMoment) ? false : true; + return !(momentNow.isAfter(endNightTime) && momentNow.isBefore(startNightMoment)); } export function doUpdateIsNight() { diff --git a/src/renderer/redux/selectors/settings.js b/src/renderer/redux/selectors/settings.js index 242fe4b7a..9c90cfac2 100644 --- a/src/renderer/redux/selectors/settings.js +++ b/src/renderer/redux/selectors/settings.js @@ -1,6 +1,5 @@ import * as SETTINGS from 'constants/settings'; import { createSelector } from 'reselect'; -import moment from 'moment'; const selectState = state => state.settings || {}; From 4409315353be7bc89ad5a79de070a1476cf7de4f Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Mon, 15 Jan 2018 05:32:01 -0800 Subject: [PATCH 06/22] Added phone verification functionality --- src/renderer/component/userFieldNew/index.js | 5 +- src/renderer/component/userFieldNew/view.jsx | 47 ++++++++++-- .../component/userFieldVerify/index.js | 9 ++- .../component/userFieldVerify/view.jsx | 19 +++-- src/renderer/component/userVerify/index.js | 5 ++ src/renderer/component/userVerify/view.jsx | 13 ++-- src/renderer/constants/action_types.js | 8 ++ src/renderer/constants/modal_types.js | 2 +- src/renderer/index.js | 4 +- .../modal/modalPhoneCollection/index.js | 7 +- .../modal/modalPhoneCollection/view.jsx | 10 +-- src/renderer/modal/modalRouter/view.jsx | 23 ++---- src/renderer/redux/actions/user.js | 75 +++++++++++++++++-- src/renderer/redux/reducers/user.js | 46 ++++++++++++ src/renderer/redux/selectors/user.js | 11 +++ 15 files changed, 225 insertions(+), 59 deletions(-) diff --git a/src/renderer/component/userFieldNew/index.js b/src/renderer/component/userFieldNew/index.js index 78c7810be..477a383e1 100644 --- a/src/renderer/component/userFieldNew/index.js +++ b/src/renderer/component/userFieldNew/index.js @@ -1,6 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; -import { doUserFieldNew, doUserInviteNew } from 'redux/actions/user'; +import { doUserEmailNew, doUserPhoneNew, doUserInviteNew } from 'redux/actions/user'; import { selectEmailNewIsPending, selectEmailNewErrorMessage } from 'redux/selectors/user'; import UserFieldNew from './view'; @@ -10,7 +10,8 @@ const select = state => ({ }); const perform = dispatch => ({ - addUserEmail: email => dispatch(doUserFieldNew(email)), + addUserEmail: email => dispatch(doUserEmailNew(email)), + addUserPhone: phone => dispatch(doUserPhoneNew(phone)), }); export default connect(select, perform)(UserFieldNew); diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index 2dd0fffd6..6cd29679a 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -6,25 +6,58 @@ class UserFieldNew extends React.PureComponent { super(props); this.state = { + phone: '', email: '', }; } - handleEmailChanged(event) { + handleChanged(event, fieldType) { + console.log({ + [fieldType]: event.target.value, + }); this.setState({ - email: event.target.value, + [fieldType]: event.target.value, }); } handleSubmit() { - const { email } = this.state; - this.props.addUserEmail(email); + const { email, phone } = this.state; + if (phone) { + this.props.addUserPhone(phone); + } else { + this.props.addUserEmail(email); + } } render() { - const { cancelButton, errorMessage, isPending } = this.props; + const { cancelButton, errorMessage, isPending, fieldType } = this.props; - return ( + return fieldType === 'phone' ? ( +
+

+ {__( + 'Enter your phone number and we will send you a verification code. We will not share your phone number with third parties.' + )} +

+
+ { + this.handleChanged(event, 'phone'); + }} + /> +
+ + {cancelButton} +
+ +
+ ) : (

{__("We'll let you know about LBRY updates, security issues, and great new content.")} @@ -39,7 +72,7 @@ class UserFieldNew extends React.PureComponent { value={this.state.email} errorMessage={errorMessage} onChange={event => { - this.handleEmailChanged(event); + this.handleChanged(event, 'email'); }} />

diff --git a/src/renderer/component/userFieldVerify/index.js b/src/renderer/component/userFieldVerify/index.js index 677df5b25..a8fab1120 100644 --- a/src/renderer/component/userFieldVerify/index.js +++ b/src/renderer/component/userFieldVerify/index.js @@ -1,9 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; -import { doUserFieldVerify, doUserFieldVerifyFailure } from 'redux/actions/user'; +import { doUserEmailVerify, doUserPhoneVerify, doUserEmailVerifyFailure } from 'redux/actions/user'; import { selectEmailVerifyIsPending, selectEmailToVerify, + selectPhoneToVerify, selectEmailVerifyErrorMessage, } from 'redux/selectors/user'; import UserFieldVerify from './view'; @@ -11,12 +12,14 @@ import UserFieldVerify from './view'; const select = state => ({ isPending: selectEmailVerifyIsPending(state), email: selectEmailToVerify(state), + phone: selectPhoneToVerify(state), errorMessage: selectEmailVerifyErrorMessage(state), }); const perform = dispatch => ({ - verifyUserEmail: (code, recaptcha) => dispatch(doUserFieldVerify(code, recaptcha)), - verifyUserEmailFailure: error => dispatch(doUserFieldVerifyFailure(error)), + verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)), + verifyUserPhone: code => dispatch(doUserPhoneVerify(code)), + verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)), }); export default connect(select, perform)(UserFieldVerify); diff --git a/src/renderer/component/userFieldVerify/view.jsx b/src/renderer/component/userFieldVerify/view.jsx index 450571b8e..0bd2e3a03 100644 --- a/src/renderer/component/userFieldVerify/view.jsx +++ b/src/renderer/component/userFieldVerify/view.jsx @@ -19,19 +19,24 @@ class UserFieldVerify extends React.PureComponent { handleSubmit() { const { code } = this.state; - try { - const verification = JSON.parse(atob(code)); - this.props.verifyUserEmail(verification.token, verification.recaptcha); - } catch (error) { - this.props.verifyUserEmailFailure('Invalid Verification Token'); + const { fieldType } = this.props; + if (fieldType === 'phone') { + this.props.verifyUserPhone(code); + } else { + try { + const verification = JSON.parse(atob(code)); + this.props.verifyUserEmail(verification.token, verification.recaptcha); + } catch (error) { + this.props.verifyUserEmailFailure('Invalid Verification Token'); + } } } render() { - const { cancelButton, errorMessage, email, isPending } = this.props; + const { cancelButton, errorMessage, email, isPending, phone } = this.props; return (
-

Please enter the verification code emailed to {email}.

+

Please enter the verification code sent to {phone || email}.

{ const selectReward = makeSelectRewardByType(); @@ -17,12 +20,14 @@ const select = (state, props) => { isPending: selectIdentityVerifyIsPending(state), errorMessage: selectIdentityVerifyErrorMessage(state), reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }), + modal: selectCurrentModal(state), }; }; const perform = dispatch => ({ navigate: uri => dispatch(doNavigate(uri)), verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)), + verifyPhone: () => dispatch(doOpenModal(PHONE_COLLECTION)), }); export default connect(select, perform)(UserVerify); diff --git a/src/renderer/component/userVerify/view.jsx b/src/renderer/component/userVerify/view.jsx index 424682013..5aa05948e 100644 --- a/src/renderer/component/userVerify/view.jsx +++ b/src/renderer/component/userVerify/view.jsx @@ -23,7 +23,7 @@ class UserVerify extends React.PureComponent { } render() { - const { errorMessage, isPending, navigate } = this.props; + const { errorMessage, isPending, navigate, verifyPhone, modal } = this.props; return (
@@ -74,12 +74,13 @@ class UserVerify extends React.PureComponent { )}`}
- {errorMessage &&

{errorMessage}

} - { + verifyPhone(); + }} + button="alt" + icon="icon-phone" label={__('Submit Phone Number')} - disabled={isPending} - token={this.onToken.bind(this)} - stripeKey={lbryio.getStripeToken()} />
diff --git a/src/renderer/constants/action_types.js b/src/renderer/constants/action_types.js index 3c828f5cb..6e0f40344 100644 --- a/src/renderer/constants/action_types.js +++ b/src/renderer/constants/action_types.js @@ -108,6 +108,14 @@ export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE'; export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED'; export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS'; export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE'; +export const USER_PHONE_DECLINE = 'USER_PHONE_DECLINE'; +export const USER_PHONE_NEW_STARTED = 'USER_PHONE_NEW_STARTED'; +export const USER_PHONE_NEW_SUCCESS = 'USER_PHONE_NEW_SUCCESS'; +export const USER_PHONE_NEW_EXISTS = 'USER_PHONE_NEW_EXISTS'; +export const USER_PHONE_NEW_FAILURE = 'USER_PHONE_NEW_FAILURE'; +export const USER_PHONE_VERIFY_STARTED = 'USER_PHONE_VERIFY_STARTED'; +export const USER_PHONE_VERIFY_SUCCESS = 'USER_PHONE_VERIFY_SUCCESS'; +export const USER_PHONE_VERIFY_FAILURE = 'USER_PHONE_VERIFY_FAILURE'; export const USER_IDENTITY_VERIFY_STARTED = 'USER_IDENTITY_VERIFY_STARTED'; export const USER_IDENTITY_VERIFY_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS'; export const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE'; diff --git a/src/renderer/constants/modal_types.js b/src/renderer/constants/modal_types.js index 9c6d457f9..995060647 100644 --- a/src/renderer/constants/modal_types.js +++ b/src/renderer/constants/modal_types.js @@ -6,7 +6,7 @@ export const ERROR = 'error'; export const INSUFFICIENT_CREDITS = 'insufficient_credits'; export const UPGRADE = 'upgrade'; export const WELCOME = 'welcome'; -export const EMAIL_COLLECTION = 'email_collection'; +export const PHONE_COLLECTION = 'phone_collection'; export const FIRST_REWARD = 'first_reward'; export const AUTHENTICATION_FAILURE = 'auth_failure'; export const TRANSACTION_FAILED = 'transaction_failed'; diff --git a/src/renderer/index.js b/src/renderer/index.js index 8d92c6ba2..fd6dcc1f7 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -12,7 +12,7 @@ import { Provider } from 'react-redux'; import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar } from 'redux/actions/app'; import { doNavigate } from 'redux/actions/navigation'; import { doDownloadLanguages } from 'redux/actions/settings'; -import { doUserFieldVerify } from 'redux/actions/user'; +import { doUserEmailVerify } from 'redux/actions/user'; import 'scss/all.scss'; import store from 'store'; import app from './app'; @@ -35,7 +35,7 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => { } if (verification.token && verification.recaptcha) { app.store.dispatch(doConditionalAuthNavigate(newSession)); - app.store.dispatch(doUserFieldVerify(verification.token, verification.recaptcha)); + app.store.dispatch(doUserEmailVerify(verification.token, verification.recaptcha)); } else { app.store.dispatch(doShowSnackBar({ message: 'Invalid Verification URI' })); } diff --git a/src/renderer/modal/modalPhoneCollection/index.js b/src/renderer/modal/modalPhoneCollection/index.js index aae6952f7..d3ae17bc6 100644 --- a/src/renderer/modal/modalPhoneCollection/index.js +++ b/src/renderer/modal/modalPhoneCollection/index.js @@ -3,18 +3,19 @@ import * as settings from 'constants/settings'; import { connect } from 'react-redux'; import { doCloseModal } from 'redux/actions/app'; import { doSetClientSetting } from 'redux/actions/settings'; -import { selectEmailToVerify, selectUser } from 'redux/selectors/user'; +import { selectPhoneToVerify, selectUser } from 'redux/selectors/user'; import ModalPhoneCollection from './view'; +import { doNavigate } from 'redux/actions/navigation'; const select = state => ({ - email: selectEmailToVerify(state), + phone: selectPhoneToVerify(state), user: selectUser(state), }); const perform = dispatch => () => ({ closeModal: () => { - dispatch(doSetClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED, true)); dispatch(doCloseModal()); + dispatch(doNavigate('/rewards')); }, }); diff --git a/src/renderer/modal/modalPhoneCollection/view.jsx b/src/renderer/modal/modalPhoneCollection/view.jsx index a8b56c844..5074387db 100644 --- a/src/renderer/modal/modalPhoneCollection/view.jsx +++ b/src/renderer/modal/modalPhoneCollection/view.jsx @@ -6,14 +6,14 @@ import UserFieldVerify from 'component/userFieldVerify'; class ModalPhoneCollection extends React.PureComponent { renderInner() { - const { closeModal, email, user } = this.props; + const { closeModal, phone, user } = this.props; const cancelButton = ; - if (!user.has_verified_email && !email) { - return ; - } else if (!user.has_verified_email) { - return ; + if (!user.phone_number && !phone) { + return ; + } else if (!user.phone_number) { + return ; } closeModal(); } diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx index 019a799fe..ac254ed6d 100644 --- a/src/renderer/modal/modalRouter/view.jsx +++ b/src/renderer/modal/modalRouter/view.jsx @@ -40,11 +40,10 @@ class ModalRouter extends React.PureComponent { return; } - const transitionModal = [ - this.checkShowWelcome, - this.checkShowEmail, - this.checkShowCreditIntro, - ].reduce((acc, func) => (!acc ? func.bind(this)(props) : acc), false); + const transitionModal = [this.checkShowWelcome, this.checkShowCreditIntro].reduce( + (acc, func) => (!acc ? func.bind(this)(props) : acc), + false + ); if ( transitionModal && @@ -65,18 +64,6 @@ class ModalRouter extends React.PureComponent { } } - checkShowEmail(props) { - const { isEmailCollectionAcknowledged, isVerificationCandidate, user } = props; - if ( - !isEmailCollectionAcknowledged && - isVerificationCandidate && - user && - !user.has_verified_email - ) { - return modals.EMAIL_COLLECTION; - } - } - checkShowCreditIntro(props) { const { balance, page, isCreditIntroAcknowledged } = props; @@ -124,7 +111,7 @@ class ModalRouter extends React.PureComponent { return ; case modals.CONFIRM_CLAIM_REVOKE: return ; - case modals.EMAIL_COLLECTION: + case modals.PHONE_COLLECTION: return ; default: return null; diff --git a/src/renderer/redux/actions/user.js b/src/renderer/redux/actions/user.js index 0c289e2ca..ba691541b 100644 --- a/src/renderer/redux/actions/user.js +++ b/src/renderer/redux/actions/user.js @@ -3,7 +3,7 @@ import * as MODALS from 'constants/modal_types'; import Lbryio from 'lbryio'; import { doOpenModal, doShowSnackBar } from 'redux/actions/app'; import { doClaimRewardType, doRewardList } from 'redux/actions/rewards'; -import { selectEmailToVerify } from 'redux/selectors/user'; +import { selectEmailToVerify, selectPhoneToVerify } from 'redux/selectors/user'; import rewards from 'rewards'; export function doFetchInviteStatus() { @@ -78,7 +78,72 @@ export function doUserFetch() { }; } -export function doUserFieldNew(email) { +export function doUserPhoneNew(phone) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_PHONE_NEW_STARTED, + phone, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_PHONE_NEW_SUCCESS, + data: { phone }, + }); + }; + + const failure = error => { + dispatch({ + type: ACTIONS.USER_PHONE_NEW_FAILURE, + data: { error }, + }); + }; + + Lbryio.call('user_phone', 'new', { phone_number: phone, country_code: 1 }, 'post').then( + success, + failure + ); + }; +} + +export function doUserPhoneVerifyFailure(error) { + return { + type: ACTIONS.USER_PHONE_VERIFY_FAILURE, + data: { error }, + }; +} + +export function doUserPhoneVerify(verificationCode) { + return (dispatch, getState) => { + const phone_number = selectPhoneToVerify(getState()); + + dispatch({ + type: ACTIONS.USER_PHONE_VERIFY_STARTED, + code: verificationCode, + }); + + Lbryio.call( + 'user_phone', + 'confirm', + { + verification_code: verificationCode, + phone_number, + country_code: '1', + }, + 'post' + ) + .then(userEmail => { + dispatch({ + type: ACTIONS.USER_PHONE_VERIFY_SUCCESS, + data: { phone_number }, + }); + dispatch(doUserFetch()); + }) + .catch(error => dispatch(doUserPhoneVerifyFailure(error))); + }; +} + +export function doUserEmailNew(email) { return dispatch => { dispatch({ type: ACTIONS.USER_EMAIL_NEW_STARTED, @@ -116,14 +181,14 @@ export function doUserFieldNew(email) { }; } -export function doUserFieldVerifyFailure(error) { +export function doUserEmailVerifyFailure(error) { return { type: ACTIONS.USER_EMAIL_VERIFY_FAILURE, data: { error }, }; } -export function doUserFieldVerify(verificationToken, recaptcha) { +export function doUserEmailVerify(verificationToken, recaptcha) { return (dispatch, getState) => { const email = selectEmailToVerify(getState()); @@ -154,7 +219,7 @@ export function doUserFieldVerify(verificationToken, recaptcha) { throw new Error('Your email is still not verified.'); // shouldn't happen } }) - .catch(error => dispatch(doUserFieldVerifyFailure(error))); + .catch(error => dispatch(doUserEmailVerifyFailure(error))); }; } diff --git a/src/renderer/redux/reducers/user.js b/src/renderer/redux/reducers/user.js index f616dd1d1..52c98b925 100644 --- a/src/renderer/redux/reducers/user.js +++ b/src/renderer/redux/reducers/user.js @@ -55,6 +55,52 @@ reducers[ACTIONS.USER_FETCH_FAILURE] = state => user: null, }); +reducers[ACTIONS.USER_PHONE_NEW_STARTED] = state => + Object.assign({}, state, { + phoneNewIsPending: true, + phoneNewErrorMessage: '', + }); + +reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) => + Object.assign({}, state, { + phoneToVerify: action.data.phone, + phoneNewIsPending: false, + }); + +reducers[ACTIONS.USER_PHONE_NEW_EXISTS] = (state, action) => + Object.assign({}, state, { + phoneToVerify: action.data.phone, + phoneNewIsPending: false, + }); + +reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) => + Object.assign({}, state, { + phoneNewIsPending: false, + phoneNewErrorMessage: action.data.error, + }); + +reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = state => + Object.assign({}, state, { + phoneVerifyIsPending: true, + phoneVerifyErrorMessage: '', + }); + +reducers[ACTIONS.USER_PHONE_VERIFY_SUCCESS] = (state, action) => { + const user = Object.assign({}, state.user); + user.phone_number = action.data.phone_number; + return Object.assign({}, state, { + phoneToVerify: '', + phoneVerifyIsPending: false, + user, + }); +}; + +reducers[ACTIONS.USER_PHONE_VERIFY_FAILURE] = (state, action) => + Object.assign({}, state, { + phoneVerifyIsPending: false, + phoneVerifyErrorMessage: action.data.error, + }); + reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state => Object.assign({}, state, { emailNewIsPending: true, diff --git a/src/renderer/redux/selectors/user.js b/src/renderer/redux/selectors/user.js index de2c6c447..638989931 100644 --- a/src/renderer/redux/selectors/user.js +++ b/src/renderer/redux/selectors/user.js @@ -16,12 +16,23 @@ export const selectUserEmail = createSelector( user => (user ? user.primary_email : null) ); +export const selectUserPhone = createSelector( + selectUser, + user => (user ? user.phone_number : null) +); + export const selectEmailToVerify = createSelector( selectState, selectUserEmail, (state, userEmail) => state.emailToVerify || userEmail ); +export const selectPhoneToVerify = createSelector( + selectState, + selectUserPhone, + (state, userPhone) => state.phoneToVerify || userPhone +); + export const selectUserIsRewardApproved = createSelector( selectUser, user => user && user.is_reward_approved From 247859a1dd6478b9bb77c12c1e7e153c8169e3aa Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Mon, 15 Jan 2018 05:35:15 -0800 Subject: [PATCH 07/22] Remove console.logs --- src/renderer/component/userFieldNew/view.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index 6cd29679a..2c2d481c8 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -12,9 +12,6 @@ class UserFieldNew extends React.PureComponent { } handleChanged(event, fieldType) { - console.log({ - [fieldType]: event.target.value, - }); this.setState({ [fieldType]: event.target.value, }); From fa7d265cc0179181a3210aefd4356bb5bcd13ea6 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Mon, 15 Jan 2018 06:30:10 -0800 Subject: [PATCH 08/22] Add error messages in phone verification modal --- src/renderer/component/userFieldNew/index.js | 9 +++++++-- src/renderer/component/userFieldNew/view.jsx | 6 +++--- src/renderer/component/userFieldVerify/index.js | 4 +++- src/renderer/component/userFieldVerify/view.jsx | 11 +++++++++-- src/renderer/redux/actions/user.js | 4 ++-- src/renderer/redux/selectors/user.js | 10 ++++++++++ 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/renderer/component/userFieldNew/index.js b/src/renderer/component/userFieldNew/index.js index 477a383e1..5cda23cc0 100644 --- a/src/renderer/component/userFieldNew/index.js +++ b/src/renderer/component/userFieldNew/index.js @@ -1,12 +1,17 @@ import React from 'react'; import { connect } from 'react-redux'; import { doUserEmailNew, doUserPhoneNew, doUserInviteNew } from 'redux/actions/user'; -import { selectEmailNewIsPending, selectEmailNewErrorMessage } from 'redux/selectors/user'; +import { + selectEmailNewIsPending, + selectEmailNewErrorMessage, + selectPhoneNewErrorMessage, +} from 'redux/selectors/user'; import UserFieldNew from './view'; const select = state => ({ isPending: selectEmailNewIsPending(state), - errorMessage: selectEmailNewErrorMessage(state), + emailErrorMessage: selectEmailNewErrorMessage(state), + phoneErrorMessage: selectPhoneNewErrorMessage(state), }); const perform = dispatch => ({ diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index 2c2d481c8..86f43cf00 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -27,7 +27,7 @@ class UserFieldNew extends React.PureComponent { } render() { - const { cancelButton, errorMessage, isPending, fieldType } = this.props; + const { cancelButton, emailErrorMessage, phoneErrorMessage, isPending, fieldType } = this.props; return fieldType === 'phone' ? (
@@ -43,7 +43,7 @@ class UserFieldNew extends React.PureComponent { placeholder="(555) 555-5555" name="phone" value={this.state.phone} - errorMessage={errorMessage} + errorMessage={phoneErrorMessage} onChange={event => { this.handleChanged(event, 'phone'); }} @@ -67,7 +67,7 @@ class UserFieldNew extends React.PureComponent { placeholder="youremail@example.org" name="email" value={this.state.email} - errorMessage={errorMessage} + errorMessage={emailErrorMessage} onChange={event => { this.handleChanged(event, 'email'); }} diff --git a/src/renderer/component/userFieldVerify/index.js b/src/renderer/component/userFieldVerify/index.js index a8fab1120..d83396d81 100644 --- a/src/renderer/component/userFieldVerify/index.js +++ b/src/renderer/component/userFieldVerify/index.js @@ -6,6 +6,7 @@ import { selectEmailToVerify, selectPhoneToVerify, selectEmailVerifyErrorMessage, + selectPhoneVerifyErrorMessage, } from 'redux/selectors/user'; import UserFieldVerify from './view'; @@ -13,7 +14,8 @@ const select = state => ({ isPending: selectEmailVerifyIsPending(state), email: selectEmailToVerify(state), phone: selectPhoneToVerify(state), - errorMessage: selectEmailVerifyErrorMessage(state), + emailErrorMessage: selectEmailVerifyErrorMessage(state), + phoneErrorMessage: selectPhoneVerifyErrorMessage(state), }); const perform = dispatch => ({ diff --git a/src/renderer/component/userFieldVerify/view.jsx b/src/renderer/component/userFieldVerify/view.jsx index 0bd2e3a03..0f09bdd78 100644 --- a/src/renderer/component/userFieldVerify/view.jsx +++ b/src/renderer/component/userFieldVerify/view.jsx @@ -33,7 +33,14 @@ class UserFieldVerify extends React.PureComponent { } render() { - const { cancelButton, errorMessage, email, isPending, phone } = this.props; + const { + cancelButton, + emailErrorMessage, + phoneErrorMessage, + email, + isPending, + phone, + } = this.props; return (

Please enter the verification code sent to {phone || email}.

@@ -45,7 +52,7 @@ class UserFieldVerify extends React.PureComponent { onChange={event => { this.handleCodeChanged(event); }} - errorMessage={errorMessage} + errorMessage={emailErrorMessage || phoneErrorMessage} /> {/* render help separately so it always shows */}
diff --git a/src/renderer/redux/actions/user.js b/src/renderer/redux/actions/user.js index ba691541b..ee99f5088 100644 --- a/src/renderer/redux/actions/user.js +++ b/src/renderer/redux/actions/user.js @@ -92,10 +92,10 @@ export function doUserPhoneNew(phone) { }); }; - const failure = error => { + const failure = () => { dispatch({ type: ACTIONS.USER_PHONE_NEW_FAILURE, - data: { error }, + data: { error: 'An error occurred while processing this phone number.' }, }); }; diff --git a/src/renderer/redux/selectors/user.js b/src/renderer/redux/selectors/user.js index 638989931..16f175345 100644 --- a/src/renderer/redux/selectors/user.js +++ b/src/renderer/redux/selectors/user.js @@ -48,6 +48,11 @@ export const selectEmailNewErrorMessage = createSelector( state => state.emailNewErrorMessage ); +export const selectPhoneNewErrorMessage = createSelector( + selectState, + state => state.phoneNewErrorMessage +); + export const selectEmailVerifyIsPending = createSelector( selectState, state => state.emailVerifyIsPending @@ -58,6 +63,11 @@ export const selectEmailVerifyErrorMessage = createSelector( state => state.emailVerifyErrorMessage ); +export const selectPhoneVerifyErrorMessage = createSelector( + selectState, + state => state.phoneVerifyErrorMessage +); + export const selectIdentityVerifyIsPending = createSelector( selectState, state => state.identityVerifyIsPending From 6f77106ffaa4a00780fbedc3cf65930a5259a11f Mon Sep 17 00:00:00 2001 From: Arrowana Date: Wed, 17 Jan 2018 20:55:25 +1100 Subject: [PATCH 09/22] is night interval to 60 seconds and shorten label message --- src/renderer/page/settings/view.jsx | 2 +- src/renderer/redux/actions/settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/page/settings/view.jsx b/src/renderer/page/settings/view.jsx index f3de1c2b3..0b061e87a 100644 --- a/src/renderer/page/settings/view.jsx +++ b/src/renderer/page/settings/view.jsx @@ -327,7 +327,7 @@ class SettingsPage extends React.PureComponent { type="checkbox" onChange={this.onAutomaticDarkModeChange.bind(this)} defaultChecked={automaticDarkModeEnabled} - label={__('Automatic dark mode (switch to dark theme between 9pm and 8am)')} + label={__('Automatic dark mode (9pm to 8am)')} />
diff --git a/src/renderer/redux/actions/settings.js b/src/renderer/redux/actions/settings.js index 2e7dd46b3..a23eb01cd 100644 --- a/src/renderer/redux/actions/settings.js +++ b/src/renderer/redux/actions/settings.js @@ -6,7 +6,7 @@ import Http from 'http'; import Lbry from 'lbry'; import moment from 'moment'; -const UPDATE_IS_NIGHT_INTERVAL = 10 * 1000; +const UPDATE_IS_NIGHT_INTERVAL = 60 * 1000; export function doFetchDaemonSettings() { return dispatch => { From faa2753e3591bf9392d34f967f4700234648b643 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Wed, 17 Jan 2018 18:22:52 -0800 Subject: [PATCH 10/22] Change to updated urls for phone verification --- src/renderer/redux/actions/user.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/renderer/redux/actions/user.js b/src/renderer/redux/actions/user.js index ee99f5088..e7b2c1e02 100644 --- a/src/renderer/redux/actions/user.js +++ b/src/renderer/redux/actions/user.js @@ -99,7 +99,7 @@ export function doUserPhoneNew(phone) { }); }; - Lbryio.call('user_phone', 'new', { phone_number: phone, country_code: 1 }, 'post').then( + Lbryio.call('user', 'phone_number_new', { phone_number: phone, country_code: 1 }, 'post').then( success, failure ); @@ -115,7 +115,7 @@ export function doUserPhoneVerifyFailure(error) { export function doUserPhoneVerify(verificationCode) { return (dispatch, getState) => { - const phone_number = selectPhoneToVerify(getState()); + const phoneNumber = selectPhoneToVerify(getState()); dispatch({ type: ACTIONS.USER_PHONE_VERIFY_STARTED, @@ -123,19 +123,19 @@ export function doUserPhoneVerify(verificationCode) { }); Lbryio.call( - 'user_phone', - 'confirm', + 'user', + 'phone_number_confirm', { verification_code: verificationCode, - phone_number, + phone_number: phoneNumber, country_code: '1', }, 'post' ) - .then(userEmail => { + .then(() => { dispatch({ type: ACTIONS.USER_PHONE_VERIFY_SUCCESS, - data: { phone_number }, + data: { phone_number: phoneNumber }, }); dispatch(doUserFetch()); }) From a34a61bce85e3ce73fd8401a0a2cdde8b10ea32d Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Wed, 17 Jan 2018 21:09:08 -0800 Subject: [PATCH 11/22] Add custom country code --- src/renderer/component/userFieldNew/index.js | 2 +- src/renderer/component/userFieldNew/view.jsx | 52 +++++++++++++++++-- .../component/userFieldVerify/index.js | 2 + .../component/userFieldVerify/view.jsx | 6 ++- src/renderer/redux/actions/user.js | 15 ++++-- src/renderer/redux/reducers/user.js | 8 ++- src/renderer/redux/selectors/user.js | 5 ++ 7 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/renderer/component/userFieldNew/index.js b/src/renderer/component/userFieldNew/index.js index 5cda23cc0..2b2dd6b96 100644 --- a/src/renderer/component/userFieldNew/index.js +++ b/src/renderer/component/userFieldNew/index.js @@ -16,7 +16,7 @@ const select = state => ({ const perform = dispatch => ({ addUserEmail: email => dispatch(doUserEmailNew(email)), - addUserPhone: phone => dispatch(doUserPhoneNew(phone)), + addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)), }); export default connect(select, perform)(UserFieldNew); diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index 86f43cf00..8fd8e4838 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -8,19 +8,53 @@ class UserFieldNew extends React.PureComponent { this.state = { phone: '', email: '', + country_code: '+1', }; + + this.formatPhone = this.formatPhone.bind(this); + } + + formatPhone(value) { + const { country_code } = this.state; + value = value.replace(/\D/g, ''); + if (country_code === '+1') { + if (!value) { + return ''; + } else if (value.length < 4) { + return value; + } else if (value.length < 7) { + return `(${value.substring(0, 3)}) ${value.substring(3)}`; + } + const fullNumber = `(${value.substring(0, 3)}) ${value.substring(3, 6)}-${value.substring( + 6 + )}`; + return fullNumber.length <= 14 ? fullNumber : fullNumber.substring(0, 14); + } + return value; + } + + formatCountryCode(value) { + if (value) { + return `+${value.replace(/\D/g, '')}`; + } + return '+1'; } handleChanged(event, fieldType) { + const formatter = { + email: _ => _, + phone: this.formatPhone, + country_code: this.formatCountryCode, + }; this.setState({ - [fieldType]: event.target.value, + [fieldType]: formatter[fieldType](event.target.value), }); } handleSubmit() { - const { email, phone } = this.state; + const { email, phone, country_code } = this.state; if (phone) { - this.props.addUserPhone(phone); + this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.replace(/\D/g, '')); } else { this.props.addUserEmail(email); } @@ -37,10 +71,20 @@ class UserFieldNew extends React.PureComponent { )}

+ { + this.handleChanged(event, 'country_code'); + }} + /> ({ isPending: selectEmailVerifyIsPending(state), email: selectEmailToVerify(state), phone: selectPhoneToVerify(state), + countryCode: selectUserCountryCode(state), emailErrorMessage: selectEmailVerifyErrorMessage(state), phoneErrorMessage: selectPhoneVerifyErrorMessage(state), }); diff --git a/src/renderer/component/userFieldVerify/view.jsx b/src/renderer/component/userFieldVerify/view.jsx index 0f09bdd78..56d78479e 100644 --- a/src/renderer/component/userFieldVerify/view.jsx +++ b/src/renderer/component/userFieldVerify/view.jsx @@ -40,10 +40,14 @@ class UserFieldVerify extends React.PureComponent { email, isPending, phone, + countryCode, } = this.props; return ( -

Please enter the verification code sent to {phone || email}.

+

+ Please enter the verification code sent to {countryCode ? `+${countryCode}` : ''} + {phone || email}. +

{ dispatch({ type: ACTIONS.USER_PHONE_NEW_STARTED, - phone, + data: { phone, country_code }, }); const success = () => { @@ -99,7 +103,7 @@ export function doUserPhoneNew(phone) { }); }; - Lbryio.call('user', 'phone_number_new', { phone_number: phone, country_code: 1 }, 'post').then( + Lbryio.call('user', 'phone_number_new', { phone_number: phone, country_code }, 'post').then( success, failure ); @@ -116,6 +120,7 @@ export function doUserPhoneVerifyFailure(error) { export function doUserPhoneVerify(verificationCode) { return (dispatch, getState) => { const phoneNumber = selectPhoneToVerify(getState()); + const countryCode = selectUserCountryCode(getState()); dispatch({ type: ACTIONS.USER_PHONE_VERIFY_STARTED, @@ -128,7 +133,7 @@ export function doUserPhoneVerify(verificationCode) { { verification_code: verificationCode, phone_number: phoneNumber, - country_code: '1', + country_code: countryCode, }, 'post' ) diff --git a/src/renderer/redux/reducers/user.js b/src/renderer/redux/reducers/user.js index 52c98b925..3d2ea165c 100644 --- a/src/renderer/redux/reducers/user.js +++ b/src/renderer/redux/reducers/user.js @@ -55,11 +55,15 @@ reducers[ACTIONS.USER_FETCH_FAILURE] = state => user: null, }); -reducers[ACTIONS.USER_PHONE_NEW_STARTED] = state => - Object.assign({}, state, { +reducers[ACTIONS.USER_PHONE_NEW_STARTED] = (state, action) => { + const user = Object.assign({}, state.user); + user.country_code = action.data.country_code; + return Object.assign({}, state, { phoneNewIsPending: true, phoneNewErrorMessage: '', + user, }); +}; reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) => Object.assign({}, state, { diff --git a/src/renderer/redux/selectors/user.js b/src/renderer/redux/selectors/user.js index 16f175345..a1b0df18e 100644 --- a/src/renderer/redux/selectors/user.js +++ b/src/renderer/redux/selectors/user.js @@ -21,6 +21,11 @@ export const selectUserPhone = createSelector( user => (user ? user.phone_number : null) ); +export const selectUserCountryCode = createSelector( + selectUser, + user => (user ? user.country_code : null) +); + export const selectEmailToVerify = createSelector( selectState, selectUserEmail, From 1da4d05986ca1c66e01ebf8757de1dde907ed95e Mon Sep 17 00:00:00 2001 From: Igor Gassmann Date: Fri, 19 Jan 2018 12:12:28 -0300 Subject: [PATCH 12/22] Refactor lbryuri.js into separate named exports --- src/renderer/component/fileCard/view.jsx | 4 +- src/renderer/component/fileList/view.jsx | 4 +- .../component/fileListSearch/view.jsx | 4 +- src/renderer/component/fileTile/view.jsx | 8 +-- .../publishForm/internal/channelSection.jsx | 4 +- src/renderer/component/publishForm/view.jsx | 8 +-- .../internal/TransactionListItem.jsx | 4 +- src/renderer/component/uriIndicator/index.js | 4 +- src/renderer/component/uriIndicator/view.jsx | 4 +- src/renderer/component/walletSend/view.jsx | 4 +- src/renderer/component/wunderbar/index.js | 4 +- src/renderer/component/wunderbar/view.jsx | 4 +- src/renderer/{lbryuri.js => lbryURI.js} | 68 +++++++++---------- src/renderer/page/channel/view.jsx | 6 +- src/renderer/page/discover/view.jsx | 4 +- src/renderer/page/file/view.jsx | 6 +- src/renderer/page/search/view.jsx | 6 +- src/renderer/page/show/view.jsx | 1 - src/renderer/redux/actions/content.js | 6 +- src/renderer/redux/actions/search.js | 4 +- src/renderer/redux/selectors/claims.js | 6 +- src/renderer/redux/selectors/media.js | 1 - src/renderer/redux/selectors/navigation.js | 4 +- 23 files changed, 80 insertions(+), 88 deletions(-) rename src/renderer/{lbryuri.js => lbryURI.js} (80%) diff --git a/src/renderer/component/fileCard/view.jsx b/src/renderer/component/fileCard/view.jsx index e38bf06c2..19b1370da 100644 --- a/src/renderer/component/fileCard/view.jsx +++ b/src/renderer/component/fileCard/view.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import lbryuri from 'lbryuri.js'; +import { normalizeURI } from 'lbryURI'; import CardMedia from 'component/cardMedia'; import Link from 'component/link'; import { TruncatedText } from 'component/common'; @@ -57,7 +57,7 @@ class FileCard extends React.PureComponent { rewardedContentClaimIds, } = this.props; - const uri = lbryuri.normalize(this.props.uri); + const uri = normalizeURI(this.props.uri); const title = metadata && metadata.title ? metadata.title : uri; const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null; const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw; diff --git a/src/renderer/component/fileList/view.jsx b/src/renderer/component/fileList/view.jsx index ba1ba296c..8dcccadc3 100644 --- a/src/renderer/component/fileList/view.jsx +++ b/src/renderer/component/fileList/view.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import lbryuri from 'lbryuri.js'; +import { buildURI } from 'lbryURI'; import FormField from 'component/formField'; import FileTile from 'component/fileTile'; import { BusyMessage } from 'component/common.js'; @@ -76,7 +76,7 @@ class FileList extends React.PureComponent { uriParams.claimId = fileInfo.claim_id; uriParams.name = fileInfo.name; } - const uri = lbryuri.build(uriParams); + const uri = buildURI(uriParams); content.push( { const { query } = props; @@ -45,7 +45,7 @@ class FileListSearch extends React.PureComponent { {uris && uris.length ? uris.map( uri => - lbryuri.parse(uri).name[0] === '@' ? ( + parseURI(uri).name[0] === '@' ? ( ) : ( diff --git a/src/renderer/component/fileTile/view.jsx b/src/renderer/component/fileTile/view.jsx index 231cfc293..6bbc3b4c6 100644 --- a/src/renderer/component/fileTile/view.jsx +++ b/src/renderer/component/fileTile/view.jsx @@ -1,6 +1,6 @@ import React from 'react'; import * as icons from 'constants/icons'; -import lbryuri from 'lbryuri.js'; +import { normalizeURI, isURIClaimable, parseURI } from 'lbryURI'; import CardMedia from 'component/cardMedia'; import { TruncatedText } from 'component/common.js'; import FilePrice from 'component/filePrice'; @@ -65,11 +65,11 @@ class FileTile extends React.PureComponent { fileInfo, } = this.props; - const uri = lbryuri.normalize(this.props.uri); + const uri = normalizeURI(this.props.uri); const isClaimed = !!claim; - const isClaimable = lbryuri.isClaimable(uri); + const isClaimable = isURIClaimable(uri); const title = - isClaimed && metadata && metadata.title ? metadata.title : lbryuri.parse(uri).contentName; + isClaimed && metadata && metadata.title ? metadata.title : parseURI(uri).contentName; const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null; const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw; const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id); diff --git a/src/renderer/component/publishForm/internal/channelSection.jsx b/src/renderer/component/publishForm/internal/channelSection.jsx index 6b4be16c6..b36c1b317 100644 --- a/src/renderer/component/publishForm/internal/channelSection.jsx +++ b/src/renderer/component/publishForm/internal/channelSection.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import lbryuri from 'lbryuri'; +import { isNameValid } from 'lbryURI'; import { FormRow } from 'component/form.js'; import { BusyMessage } from 'component/common'; import Link from 'component/link'; @@ -29,7 +29,7 @@ class ChannelSection extends React.PureComponent { ? event.target.value : `@${event.target.value}`; - if (newChannelName.length > 1 && !lbryuri.isValidName(newChannelName.substr(1), false)) { + if (newChannelName.length > 1 && !isNameValid(newChannelName.substr(1), false)) { this.refs.newChannelName.showError( __('LBRY channel names must contain only letters, numbers and dashes.') ); diff --git a/src/renderer/component/publishForm/view.jsx b/src/renderer/component/publishForm/view.jsx index 1d407f908..4f770a803 100644 --- a/src/renderer/component/publishForm/view.jsx +++ b/src/renderer/component/publishForm/view.jsx @@ -1,6 +1,6 @@ import React from 'react'; import lbry from 'lbry'; -import lbryuri from 'lbryuri'; +import { isNameValid, buildURI, regexInvalidURI } from 'lbryURI'; import FormField from 'component/formField'; import { Form, FormRow, Submit } from 'component/form.js'; import Link from 'component/link'; @@ -245,7 +245,7 @@ class PublishForm extends React.PureComponent { return; } - if (!lbryuri.isValidName(rawName, false)) { + if (!isNameValid(rawName, false)) { this.refs.name.showError(__('LBRY names must contain only letters, numbers and dashes.')); return; } @@ -254,7 +254,7 @@ class PublishForm extends React.PureComponent { if (this.state.channel !== 'anonymous') channel = this.state.channel; const name = rawName.toLowerCase(); - const uri = lbryuri.build({ contentName: name, channelName: channel }); + const uri = buildURI({ contentName: name, channelName: channel }); this.setState({ rawName, name, @@ -446,7 +446,7 @@ class PublishForm extends React.PureComponent { const extension = path.extname(fileName); fileName = path.basename(fileName, extension); - fileName = fileName.replace(lbryuri.REGEXP_INVALID_URI, ''); + fileName = fileName.replace(regexInvalidURI, ''); return fileName; } diff --git a/src/renderer/component/transactionList/internal/TransactionListItem.jsx b/src/renderer/component/transactionList/internal/TransactionListItem.jsx index ce4a1b215..0192e33d1 100644 --- a/src/renderer/component/transactionList/internal/TransactionListItem.jsx +++ b/src/renderer/component/transactionList/internal/TransactionListItem.jsx @@ -3,7 +3,7 @@ import LinkTransaction from 'component/linkTransaction'; import { CreditAmount } from 'component/common'; import DateTime from 'component/dateTime'; import Link from 'component/link'; -import lbryuri from 'lbryuri'; +import { buildURI } from 'lbryURI'; import * as txnTypes from 'constants/transaction_types'; class TransactionListItem extends React.PureComponent { @@ -74,7 +74,7 @@ class TransactionListItem extends React.PureComponent { {name} diff --git a/src/renderer/component/uriIndicator/index.js b/src/renderer/component/uriIndicator/index.js index b5e9162f0..298a90696 100644 --- a/src/renderer/component/uriIndicator/index.js +++ b/src/renderer/component/uriIndicator/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import lbryuri from 'lbryuri'; +import { normalizeURI } from 'lbryURI'; import { connect } from 'react-redux'; import { doResolveUri } from 'redux/actions/content'; import { makeSelectIsUriResolving } from 'redux/selectors/content'; @@ -9,7 +9,7 @@ import UriIndicator from './view'; const select = (state, props) => ({ claim: makeSelectClaimForUri(props.uri)(state), isResolvingUri: makeSelectIsUriResolving(props.uri)(state), - uri: lbryuri.normalize(props.uri), + uri: normalizeURI(props.uri), }); const perform = dispatch => ({ diff --git a/src/renderer/component/uriIndicator/view.jsx b/src/renderer/component/uriIndicator/view.jsx index 704769250..932ff7e5c 100644 --- a/src/renderer/component/uriIndicator/view.jsx +++ b/src/renderer/component/uriIndicator/view.jsx @@ -1,7 +1,7 @@ import React from 'react'; import Icon from 'component/icon'; import Link from 'component/link'; -import lbryuri from 'lbryuri'; +import { buildURI } from 'lbryURI'; import classnames from 'classnames'; class UriIndicator extends React.PureComponent { @@ -49,7 +49,7 @@ class UriIndicator extends React.PureComponent { if (signatureIsValid) { modifier = 'valid'; - channelLink = link ? lbryuri.build({ channelName, claimId: channelClaimId }, false) : false; + channelLink = link ? buildURI({ channelName, claimId: channelClaimId }, false) : false; } else { icon = 'icon-times-circle'; modifier = 'invalid'; diff --git a/src/renderer/component/walletSend/view.jsx b/src/renderer/component/walletSend/view.jsx index 673e7e0f7..7c51953b1 100644 --- a/src/renderer/component/walletSend/view.jsx +++ b/src/renderer/component/walletSend/view.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Form, FormRow, Submit } from 'component/form'; -import lbryuri from 'lbryuri'; +import { regexAddress } from 'lbryURI'; class WalletSend extends React.PureComponent { handleSubmit() { @@ -42,7 +42,7 @@ class WalletSend extends React.PureComponent { size="60" onChange={setAddress} value={address} - regexp={lbryuri.REGEXP_ADDRESS} + regexp={regexAddress} trim />
diff --git a/src/renderer/component/wunderbar/index.js b/src/renderer/component/wunderbar/index.js index 826ab4225..c446a885e 100644 --- a/src/renderer/component/wunderbar/index.js +++ b/src/renderer/component/wunderbar/index.js @@ -1,6 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; -import lbryuri from 'lbryuri.js'; +import { normalizeURI } from 'lbryURI.js'; import { selectWunderBarAddress, selectWunderBarIcon } from 'redux/selectors/search'; import { doNavigate } from 'redux/actions/navigation'; import Wunderbar from './view'; @@ -13,7 +13,7 @@ const select = state => ({ const perform = dispatch => ({ onSearch: query => dispatch(doNavigate('/search', { query })), onSubmit: (query, extraParams) => - dispatch(doNavigate('/show', { uri: lbryuri.normalize(query), ...extraParams })), + dispatch(doNavigate('/show', { uri: normalizeURI(query), ...extraParams })), }); export default connect(select, perform)(Wunderbar); diff --git a/src/renderer/component/wunderbar/view.jsx b/src/renderer/component/wunderbar/view.jsx index f332a11ec..29cbc7418 100644 --- a/src/renderer/component/wunderbar/view.jsx +++ b/src/renderer/component/wunderbar/view.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import lbryuri from 'lbryuri.js'; +import { normalizeURI } from 'lbryURI'; import Icon from 'component/icon'; import { parseQueryParams } from 'util/query_params'; @@ -130,7 +130,7 @@ class WunderBar extends React.PureComponent { if (parts.length > 0) extraParams = parseQueryParams(parts.join('')); try { - uri = lbryuri.normalize(value); + uri = normalizeURI(value); this.setState({ value: uri }); } catch (error) { // then it's not a valid URL, so let's search diff --git a/src/renderer/lbryuri.js b/src/renderer/lbryURI.js similarity index 80% rename from src/renderer/lbryuri.js rename to src/renderer/lbryURI.js index 3dc934770..e0691965e 100644 --- a/src/renderer/lbryuri.js +++ b/src/renderer/lbryURI.js @@ -1,10 +1,8 @@ -const CHANNEL_NAME_MIN_LEN = 1; -const CLAIM_ID_MAX_LEN = 40; +const channelNameMinLength = 1; +const claimIdMaxLength = 40; -const Lbryuri = {}; - -Lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g; -Lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/; +export const regexInvalidURI = /[^A-Za-z0-9-]/g; +export const regexAddress = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/; /** * Parses a LBRY name into its component parts. Throws errors with user-friendly @@ -28,7 +26,7 @@ Lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/; * - contentName (string): For anon claims, the name; for channel claims, the path * - channelName (string, if present): Channel name without @ */ -Lbryuri.parse = (uri, requireProto = false) => { +export function parseURI(URI, requireProto = false) { // Break into components. Empty sub-matches are converted to null const componentsRegex = new RegExp( '^((?:lbry://)?)' + // protocol @@ -37,7 +35,7 @@ Lbryuri.parse = (uri, requireProto = false) => { '(/?)(.*)' // path separator, path ); const [proto, name, modSep, modVal, pathSep, path] = componentsRegex - .exec(uri) + .exec(URI) .slice(1) .map(match => match || null); @@ -61,14 +59,14 @@ Lbryuri.parse = (uri, requireProto = false) => { throw new Error(__('No channel name after @.')); } - if (channelName.length < CHANNEL_NAME_MIN_LEN) { - throw new Error(__(`Channel names must be at least %s characters.`, CHANNEL_NAME_MIN_LEN)); + if (channelName.length < channelNameMinLength) { + throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength)); } contentName = path; } - const nameBadChars = (channelName || name).match(Lbryuri.REGEXP_INVALID_URI); + const nameBadChars = (channelName || name).match(regexInvalidURI); if (nameBadChars) { throw new Error( __( @@ -99,7 +97,7 @@ Lbryuri.parse = (uri, requireProto = false) => { if ( claimId && - (claimId.length > CLAIM_ID_MAX_LEN || !claimId.match(/^[0-9a-f]+$/)) && + (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/)) && !claimId.match(/^pending/) // ought to be dropped when savePendingPublish drops hack ) { throw new Error(__(`Invalid claim ID %s.`, claimId)); @@ -119,7 +117,7 @@ Lbryuri.parse = (uri, requireProto = false) => { throw new Error(__('Only channel URIs may have a path.')); } - const pathBadChars = path.match(Lbryuri.REGEXP_INVALID_URI); + const pathBadChars = path.match(regexInvalidURI); if (pathBadChars) { throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', '))); } @@ -140,17 +138,17 @@ Lbryuri.parse = (uri, requireProto = false) => { ...(claimId ? { claimId } : {}), ...(path ? { path } : {}), }; -}; +} /** - * Takes an object in the same format returned by lbryuri.parse() and builds a URI. + * Takes an object in the same format returned by parse() and builds a URI. * * The channelName key will accept names with or without the @ prefix. */ -Lbryuri.build = (uriObj, includeProto = true) => { - const { claimId, claimSequence, bidPosition, contentName, channelName } = uriObj; +export function buildURI(URIObj, includeProto = true) { + const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj; - let { name, path } = uriObj; + let { name, path } = URIObj; if (channelName) { const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`; @@ -188,36 +186,35 @@ Lbryuri.build = (uriObj, includeProto = true) => { (bidPosition ? `${bidPosition}` : '') + (path ? `/${path}` : '') ); -}; +} -/* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just - * consists of adding the lbry:// prefix if needed) */ -Lbryuri.normalize = uri => { - if (uri.match(/pending_claim/)) return uri; +/* Takes a parseable LBRY URI and converts it to standard, canonical format */ +export function normalizeURI(URI) { + if (URI.match(/pending_claim/)) return URI; - const { name, path, bidPosition, claimSequence, claimId } = Lbryuri.parse(uri); - return Lbryuri.build({ name, path, claimSequence, bidPosition, claimId }); -}; + const { name, path, bidPosition, claimSequence, claimId } = parseURI(URI); + return buildURI({ name, path, claimSequence, bidPosition, claimId }); +} -Lbryuri.isValid = uri => { +export function isURIValid(URI) { let parts; try { - parts = Lbryuri.parse(Lbryuri.normalize(uri)); + parts = parseURI(normalizeURI(URI)); } catch (error) { return false; } return parts && parts.name; -}; +} -Lbryuri.isValidName = (name, checkCase = true) => { +export function isNameValid(name, checkCase = true) { const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i'); return regexp.test(name); -}; +} -Lbryuri.isClaimable = uri => { +export function isURIClaimable(URI) { let parts; try { - parts = Lbryuri.parse(Lbryuri.normalize(uri)); + parts = parseURI(normalizeURI(URI)); } catch (error) { return false; } @@ -230,7 +227,4 @@ Lbryuri.isClaimable = uri => { !parts.isChannel && !parts.path ); -}; - -window.lbryuri = Lbryuri; -export default Lbryuri; +} diff --git a/src/renderer/page/channel/view.jsx b/src/renderer/page/channel/view.jsx index f32a9f938..ae1f6b332 100644 --- a/src/renderer/page/channel/view.jsx +++ b/src/renderer/page/channel/view.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import lbryuri from 'lbryuri'; +import { buildURI } from 'lbryURI'; import { BusyMessage } from 'component/common'; import FileTile from 'component/fileTile'; import ReactPaginate from 'react-paginate'; @@ -46,7 +46,7 @@ class ChannelPage extends React.PureComponent { } = this.props; const { name, claim_id: claimId } = claim; - const subscriptionUri = lbryuri.build({ channelName: name, claimId }, false); + const subscriptionUri = buildURI({ channelName: name, claimId }, false); let contentList; if (fetching) { @@ -57,7 +57,7 @@ class ChannelPage extends React.PureComponent { claimsInChannel.map(claim => ( {names && names.map(name => ( - + ))}
diff --git a/src/renderer/page/file/view.jsx b/src/renderer/page/file/view.jsx index fb8f741c8..e248ee5d6 100644 --- a/src/renderer/page/file/view.jsx +++ b/src/renderer/page/file/view.jsx @@ -1,6 +1,6 @@ import React from 'react'; import lbry from 'lbry'; -import lbryuri from 'lbryuri'; +import { buildURI, normalizeURI } from 'lbryURI'; import Video from 'component/video'; import { Thumbnail } from 'component/common'; import FilePrice from 'component/filePrice'; @@ -65,7 +65,7 @@ class FilePage extends React.PureComponent { let subscriptionUri; if (channelName && channelClaimId) { - subscriptionUri = lbryuri.build({ channelName, claimId: channelClaimId }, false); + subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false); } return ( @@ -86,7 +86,7 @@ class FilePage extends React.PureComponent {
{!fileInfo || fileInfo.written_bytes <= 0 ? ( - + {isRewardContent && ( {' '} diff --git a/src/renderer/page/search/view.jsx b/src/renderer/page/search/view.jsx index 48aed6fce..4f9cae040 100644 --- a/src/renderer/page/search/view.jsx +++ b/src/renderer/page/search/view.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import lbryuri from 'lbryuri'; +import { isURIValid, normalizeURI } from 'lbryURI'; import FileTile from 'component/fileTile'; import FileListSearch from 'component/fileListSearch'; import { ToolTip } from 'component/tooltip.js'; @@ -10,7 +10,7 @@ class SearchPage extends React.PureComponent { return (
- {lbryuri.isValid(query) ? ( + {isURIValid(query) ? (

{__('Exact URL')}{' '} @@ -20,7 +20,7 @@ class SearchPage extends React.PureComponent { className="tooltip--header" />

- +
) : ( '' diff --git a/src/renderer/page/show/view.jsx b/src/renderer/page/show/view.jsx index ebb45da30..65799a7ee 100644 --- a/src/renderer/page/show/view.jsx +++ b/src/renderer/page/show/view.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import lbryuri from 'lbryuri'; import { BusyMessage } from 'component/common'; import ChannelPage from 'page/channel'; import FilePage from 'page/file'; diff --git a/src/renderer/redux/actions/content.js b/src/renderer/redux/actions/content.js index 7309f1c13..2ca2c4dbb 100644 --- a/src/renderer/redux/actions/content.js +++ b/src/renderer/redux/actions/content.js @@ -4,7 +4,7 @@ import * as SETTINGS from 'constants/settings'; import { ipcRenderer } from 'electron'; import Lbry from 'lbry'; import Lbryio from 'lbryio'; -import Lbryuri from 'lbryuri'; +import { normalizeURI, buildURI } from 'lbryURI'; import { doAlertError, doOpenModal } from 'redux/actions/app'; import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards'; import { selectBadgeNumber } from 'redux/selectors/app'; @@ -26,7 +26,7 @@ const DOWNLOAD_POLL_INTERVAL = 250; export function doResolveUris(uris) { return (dispatch, getState) => { - const normalizedUris = uris.map(Lbryuri.normalize); + const normalizedUris = uris.map(normalizeURI); const state = getState(); // Filter out URIs that are already resolving @@ -506,7 +506,7 @@ export function doAbandonClaim(txid, nout) { claimId, }, }); - dispatch(doResolveUri(Lbryuri.build({ name, claimId }))); + dispatch(doResolveUri(buildURI({ name, claimId }))); dispatch(doFetchClaimListMine()); } else { dispatch(doOpenModal(MODALS.TRANSACTION_FAILED)); diff --git a/src/renderer/redux/actions/search.js b/src/renderer/redux/actions/search.js index 277b5cbe4..e8922f650 100644 --- a/src/renderer/redux/actions/search.js +++ b/src/renderer/redux/actions/search.js @@ -1,5 +1,5 @@ import * as ACTIONS from 'constants/action_types'; -import Lbryuri from 'lbryuri'; +import { buildURI } from 'lbryURI'; import { doResolveUri } from 'redux/actions/content'; import { doNavigate } from 'redux/actions/navigation'; import { selectCurrentPage } from 'redux/selectors/navigation'; @@ -40,7 +40,7 @@ export function doSearch(rawQuery) { const actions = []; data.forEach(result => { - const uri = Lbryuri.build({ + const uri = buildURI({ name: result.name, claimId: result.claimId, }); diff --git a/src/renderer/redux/selectors/claims.js b/src/renderer/redux/selectors/claims.js index 4ee7faad8..9da8a6d51 100644 --- a/src/renderer/redux/selectors/claims.js +++ b/src/renderer/redux/selectors/claims.js @@ -1,4 +1,4 @@ -import Lbryuri from 'lbryuri'; +import { normalizeURI } from 'lbryURI'; import { makeSelectCurrentParam } from 'redux/selectors/navigation'; import { createSelector } from 'reselect'; @@ -32,7 +32,7 @@ export const selectAllClaimsByChannel = createSelector( ); export const makeSelectClaimForUri = uri => - createSelector(selectClaimsByUri, claims => claims && claims[Lbryuri.normalize(uri)]); + createSelector(selectClaimsByUri, claims => claims && claims[normalizeURI(uri)]); export const selectMyClaimsRaw = createSelector(selectState, state => state.myClaims); @@ -53,7 +53,7 @@ export const selectMyActiveClaims = createSelector( ); export const makeSelectClaimIsMine = rawUri => { - const uri = Lbryuri.normalize(rawUri); + const uri = normalizeURI(rawUri); return createSelector( selectClaimsByUri, selectMyActiveClaims, diff --git a/src/renderer/redux/selectors/media.js b/src/renderer/redux/selectors/media.js index e6cc54a71..804b1190b 100644 --- a/src/renderer/redux/selectors/media.js +++ b/src/renderer/redux/selectors/media.js @@ -1,6 +1,5 @@ import * as settings from 'constants/settings'; import { createSelector } from 'reselect'; -import lbryuri from 'lbryuri'; import { makeSelectClaimForUri } from 'redux/selectors/claims'; const _selectState = state => state.media || {}; diff --git a/src/renderer/redux/selectors/navigation.js b/src/renderer/redux/selectors/navigation.js index dc0b507a1..fdee0783d 100644 --- a/src/renderer/redux/selectors/navigation.js +++ b/src/renderer/redux/selectors/navigation.js @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { parseQueryParams, toQueryString } from 'util/query_params'; -import Lbryuri from 'lbryuri'; +import { normalizeURI } from 'lbryURI'; export const selectState = state => state.navigation || {}; @@ -93,7 +93,7 @@ export const selectPageTitle = createSelector( case 'developer': return __('Developer'); case 'show': { - const parts = [Lbryuri.normalize(params.uri)]; + const parts = [normalizeURI(params.uri)]; // If the params has any keys other than "uri" if (Object.keys(params).length > 1) { parts.push(toQueryString(Object.assign({}, params, { uri: null }))); From a5dce3e77a5109632892d16c308394e4f525dcba Mon Sep 17 00:00:00 2001 From: Arrowana Date: Sat, 20 Jan 2018 08:37:22 +1100 Subject: [PATCH 13/22] isNight() to arrow function and update isNight every 10 minutes, default to false --- src/renderer/redux/actions/settings.js | 18 +++++++++--------- src/renderer/redux/reducers/settings.js | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/renderer/redux/actions/settings.js b/src/renderer/redux/actions/settings.js index a23eb01cd..9397efada 100644 --- a/src/renderer/redux/actions/settings.js +++ b/src/renderer/redux/actions/settings.js @@ -6,7 +6,7 @@ import Http from 'http'; import Lbry from 'lbry'; import moment from 'moment'; -const UPDATE_IS_NIGHT_INTERVAL = 60 * 1000; +const UPDATE_IS_NIGHT_INTERVAL = 10 * 60 * 1000; export function doFetchDaemonSettings() { return dispatch => { @@ -56,6 +56,7 @@ export function doGetThemes() { export function doUpdateIsNightAsync() { return dispatch => { + dispatch(doUpdateIsNight()); const updateIsNightInterval = setInterval( () => dispatch(doUpdateIsNight()), UPDATE_IS_NIGHT_INTERVAL @@ -63,17 +64,16 @@ export function doUpdateIsNightAsync() { }; } -function isNight() { - const startNightMoment = moment('19:00', 'HH:mm'); - const endNightTime = moment('8:00', 'HH:mm'); - const momentNow = moment(); - return !(momentNow.isAfter(endNightTime) && momentNow.isBefore(startNightMoment)); -} - export function doUpdateIsNight() { + const momentNow = moment(); return { type: ACTIONS.UPDATE_IS_NIGHT, - data: { isNight: isNight() }, + data: { isNight: () => { + const startNightMoment = moment('19:00', 'HH:mm'); + const endNightMoment = moment('8:00', 'HH:mm'); + return !(momentNow.isAfter(endNightMoment) && momentNow.isBefore(startNightMoment)); + } + }, }; } diff --git a/src/renderer/redux/reducers/settings.js b/src/renderer/redux/reducers/settings.js index a3e6e0536..73cf69209 100644 --- a/src/renderer/redux/reducers/settings.js +++ b/src/renderer/redux/reducers/settings.js @@ -25,7 +25,7 @@ const defaultState = { themes: getLocalStorageSetting(SETTINGS.THEMES, []), automaticDarkModeEnabled: getLocalStorageSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED, false), }, - isNight: true, + isNight: false, languages: {}, }; From 3ac913a28eead88a25262be86dfbc089a8faa4d6 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Sun, 21 Jan 2018 11:29:48 -0800 Subject: [PATCH 14/22] Add new phone select style --- src/renderer/component/userFieldNew/view.jsx | 37 +++++++++----------- src/renderer/scss/_vars.scss | 1 + src/renderer/scss/component/_form-field.scss | 10 +++++- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index 8fd8e4838..c9b7ceb34 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -1,5 +1,6 @@ import React from 'react'; import { Form, FormRow, Submit } from 'component/form.js'; +import FormField from 'component/formField'; class UserFieldNew extends React.PureComponent { constructor(props) { @@ -71,27 +72,21 @@ class UserFieldNew extends React.PureComponent { )}

- { - this.handleChanged(event, 'country_code'); - }} - /> - { - this.handleChanged(event, 'phone'); - }} - /> +
+ + + + { + this.handleChanged(event, 'phone'); + }} + /> +
{cancelButton} diff --git a/src/renderer/scss/_vars.scss b/src/renderer/scss/_vars.scss index 15436a5b1..f05049912 100644 --- a/src/renderer/scss/_vars.scss +++ b/src/renderer/scss/_vars.scss @@ -79,6 +79,7 @@ $text-color: #000; /* Select */ --select-bg: var(--color-bg-alt); --select-color: var(--text-color); + --select-height: 30px; /* Button */ --button-bg: var(--color-bg-alt); diff --git a/src/renderer/scss/component/_form-field.scss b/src/renderer/scss/component/_form-field.scss index 042565611..944aa9730 100644 --- a/src/renderer/scss/component/_form-field.scss +++ b/src/renderer/scss/component/_form-field.scss @@ -5,6 +5,14 @@ margin-bottom: $spacing-vertical; } +.form-row-phone { + display: flex; + + select { + margin-right: 5px; + } +} + .form-row__label-row { margin-top: $spacing-vertical * 5/6; margin-bottom: 0px; @@ -32,7 +40,7 @@ box-sizing: border-box; padding-left: 5px; padding-right: 5px; - height: $spacing-vertical; + height: var(--select-height); background: var(--select-bg); color: var(--select-color); &:focus { From 705a74e2ae115e24f34cbe66d9954b1c7f389c48 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Sun, 21 Jan 2018 22:17:06 -0800 Subject: [PATCH 15/22] Functioning select phone input side by side with supposed emoji flag support --- package.json | 1 + src/renderer/component/userFieldNew/view.jsx | 30 ++++++++++++++++++-- src/renderer/scss/component/_form-field.scss | 5 ++-- yarn.lock | 15 ++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2e61b99cc..3906049d8 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "amplitude-js": "^4.0.0", "bluebird": "^3.5.1", "classnames": "^2.2.5", + "country-data": "^0.0.31", "electron-dl": "^1.6.0", "formik": "^0.10.4", "from2": "^2.3.0", diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index c9b7ceb34..1824359e8 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -2,6 +2,22 @@ import React from 'react'; import { Form, FormRow, Submit } from 'component/form.js'; import FormField from 'component/formField'; +const countryCodes = require('country-data') + .callingCountries.all.filter(_ => _.emoji) + .reduce( + (acc, cur) => acc.concat(cur.countryCallingCodes.map(_ => ({ ...cur, countryCallingCode: _ }))), + [] + ) + .sort((a, b) => { + if (a.countryCallingCodes[0] < b.countryCallingCodes[0]) { + return -1; + } + if (a.countryCallingCodes[0] > b.countryCallingCodes[0]) { + return 1; + } + return 0; + }); + class UserFieldNew extends React.PureComponent { constructor(props) { super(props); @@ -52,10 +68,14 @@ class UserFieldNew extends React.PureComponent { }); } + handleSelect(event) { + this.setState({ country_code: event.target.value }); + } + handleSubmit() { const { email, phone, country_code } = this.state; if (phone) { - this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.replace(/\D/g, '')); + this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.substring(1)); } else { this.props.addUserEmail(email); } @@ -73,8 +93,12 @@ class UserFieldNew extends React.PureComponent {

- - + + {countryCodes.map(country => ( + + ))} 1.4.4" + create-ecdh@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" @@ -2406,6 +2413,10 @@ csso@~2.3.1: clap "^1.0.9" source-map "^0.5.3" +currency-symbol-map@~2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/currency-symbol-map/-/currency-symbol-map-2.2.0.tgz#2b3c1872ff1ac2ce595d8273e58e1fff0272aea2" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -8781,6 +8792,10 @@ unc-path-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" +underscore@>1.4.4: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" From 99bdaa608efae8a4c6c2a4237884df22a1bcbdea Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Sun, 21 Jan 2018 22:30:20 -0800 Subject: [PATCH 16/22] Small fixes to country code select --- src/renderer/component/userFieldNew/view.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userFieldNew/view.jsx index 1824359e8..1bcc03db6 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userFieldNew/view.jsx @@ -9,10 +9,10 @@ const countryCodes = require('country-data') [] ) .sort((a, b) => { - if (a.countryCallingCodes[0] < b.countryCallingCodes[0]) { + if (a.countryCallingCode < b.countryCallingCode) { return -1; } - if (a.countryCallingCodes[0] > b.countryCallingCodes[0]) { + if (a.countryCallingCode > b.countryCallingCode) { return 1; } return 0; @@ -94,8 +94,8 @@ class UserFieldNew extends React.PureComponent {
- {countryCodes.map(country => ( - ))} From 9e2d82eac83bfac60dc6650ce128590e22dd6823 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Sun, 21 Jan 2018 23:09:11 -0800 Subject: [PATCH 17/22] Split userField... into userEmail... and userPhone... --- src/renderer/component/userEmailNew/index.js | 16 +++++ src/renderer/component/userEmailNew/view.jsx | 55 +++++++++++++++++ .../component/userEmailVerify/index.js | 22 +++++++ .../view.jsx | 36 ++++------- src/renderer/component/userFieldNew/index.js | 22 ------- .../component/userFieldVerify/index.js | 29 --------- src/renderer/component/userPhoneNew/index.js | 15 +++++ .../{userFieldNew => userPhoneNew}/view.jsx | 59 +++--------------- .../component/userPhoneVerify/index.js | 21 +++++++ .../component/userPhoneVerify/view.jsx | 60 +++++++++++++++++++ .../modal/modalPhoneCollection/view.jsx | 8 +-- src/renderer/page/auth/view.jsx | 8 +-- 12 files changed, 216 insertions(+), 135 deletions(-) create mode 100644 src/renderer/component/userEmailNew/index.js create mode 100644 src/renderer/component/userEmailNew/view.jsx create mode 100644 src/renderer/component/userEmailVerify/index.js rename src/renderer/component/{userFieldVerify => userEmailVerify}/view.jsx (60%) delete mode 100644 src/renderer/component/userFieldNew/index.js delete mode 100644 src/renderer/component/userFieldVerify/index.js create mode 100644 src/renderer/component/userPhoneNew/index.js rename src/renderer/component/{userFieldNew => userPhoneNew}/view.jsx (61%) create mode 100644 src/renderer/component/userPhoneVerify/index.js create mode 100644 src/renderer/component/userPhoneVerify/view.jsx diff --git a/src/renderer/component/userEmailNew/index.js b/src/renderer/component/userEmailNew/index.js new file mode 100644 index 000000000..0228a4aaa --- /dev/null +++ b/src/renderer/component/userEmailNew/index.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { doUserEmailNew, doUserInviteNew } from 'redux/actions/user'; +import { selectEmailNewIsPending, selectEmailNewErrorMessage } from 'redux/selectors/user'; +import UserEmailNew from './view'; + +const select = state => ({ + isPending: selectEmailNewIsPending(state), + errorMessage: selectEmailNewErrorMessage(state), +}); + +const perform = dispatch => ({ + addUserEmail: email => dispatch(doUserEmailNew(email)), +}); + +export default connect(select, perform)(UserEmailNew); diff --git a/src/renderer/component/userEmailNew/view.jsx b/src/renderer/component/userEmailNew/view.jsx new file mode 100644 index 000000000..661adb611 --- /dev/null +++ b/src/renderer/component/userEmailNew/view.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Form, FormRow, Submit } from 'component/form.js'; + +class UserEmailNew extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + email: '', + }; + } + + handleEmailChanged(event) { + this.setState({ + email: event.target.value, + }); + } + + handleSubmit() { + const { email } = this.state; + this.props.addUserEmail(email); + } + + render() { + const { cancelButton, errorMessage, isPending } = this.props; + + return ( +
+

+ {__("We'll let you know about LBRY updates, security issues, and great new content.")} +

+

{__("We'll never sell your email, and you can unsubscribe at any time.")}

+ + { + this.handleEmailChanged(event); + }} + /> +
+ + {cancelButton} +
+ +
+ ); + } +} + +export default UserEmailNew; diff --git a/src/renderer/component/userEmailVerify/index.js b/src/renderer/component/userEmailVerify/index.js new file mode 100644 index 000000000..f8c854ca1 --- /dev/null +++ b/src/renderer/component/userEmailVerify/index.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { doUserEmailVerify, doUserEmailVerifyFailure } from 'redux/actions/user'; +import { + selectEmailVerifyIsPending, + selectEmailToVerify, + selectEmailVerifyErrorMessage, +} from 'redux/selectors/user'; +import UserEmailVerify from './view'; + +const select = state => ({ + isPending: selectEmailVerifyIsPending(state), + email: selectEmailToVerify(state), + errorMessage: selectEmailVerifyErrorMessage(state), +}); + +const perform = dispatch => ({ + verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)), + verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)), +}); + +export default connect(select, perform)(UserEmailVerify); diff --git a/src/renderer/component/userFieldVerify/view.jsx b/src/renderer/component/userEmailVerify/view.jsx similarity index 60% rename from src/renderer/component/userFieldVerify/view.jsx rename to src/renderer/component/userEmailVerify/view.jsx index 56d78479e..3304730b9 100644 --- a/src/renderer/component/userFieldVerify/view.jsx +++ b/src/renderer/component/userEmailVerify/view.jsx @@ -2,7 +2,7 @@ import React from 'react'; import Link from 'component/link'; import { Form, FormRow, Submit } from 'component/form.js'; -class UserFieldVerify extends React.PureComponent { +class UserEmailVerify extends React.PureComponent { constructor(props) { super(props); @@ -19,35 +19,19 @@ class UserFieldVerify extends React.PureComponent { handleSubmit() { const { code } = this.state; - const { fieldType } = this.props; - if (fieldType === 'phone') { - this.props.verifyUserPhone(code); - } else { - try { - const verification = JSON.parse(atob(code)); - this.props.verifyUserEmail(verification.token, verification.recaptcha); - } catch (error) { - this.props.verifyUserEmailFailure('Invalid Verification Token'); - } + try { + const verification = JSON.parse(atob(code)); + this.props.verifyUserEmail(verification.token, verification.recaptcha); + } catch (error) { + this.props.verifyUserEmailFailure('Invalid Verification Token'); } } render() { - const { - cancelButton, - emailErrorMessage, - phoneErrorMessage, - email, - isPending, - phone, - countryCode, - } = this.props; + const { cancelButton, errorMessage, email, isPending } = this.props; return (
-

- Please enter the verification code sent to {countryCode ? `+${countryCode}` : ''} - {phone || email}. -

+

Please enter the verification code emailed to {email}.

{ this.handleCodeChanged(event); }} - errorMessage={emailErrorMessage || phoneErrorMessage} + errorMessage={errorMessage} /> {/* render help separately so it always shows */}
@@ -75,4 +59,4 @@ class UserFieldVerify extends React.PureComponent { } } -export default UserFieldVerify; +export default UserEmailVerify; diff --git a/src/renderer/component/userFieldNew/index.js b/src/renderer/component/userFieldNew/index.js deleted file mode 100644 index 2b2dd6b96..000000000 --- a/src/renderer/component/userFieldNew/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { doUserEmailNew, doUserPhoneNew, doUserInviteNew } from 'redux/actions/user'; -import { - selectEmailNewIsPending, - selectEmailNewErrorMessage, - selectPhoneNewErrorMessage, -} from 'redux/selectors/user'; -import UserFieldNew from './view'; - -const select = state => ({ - isPending: selectEmailNewIsPending(state), - emailErrorMessage: selectEmailNewErrorMessage(state), - phoneErrorMessage: selectPhoneNewErrorMessage(state), -}); - -const perform = dispatch => ({ - addUserEmail: email => dispatch(doUserEmailNew(email)), - addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)), -}); - -export default connect(select, perform)(UserFieldNew); diff --git a/src/renderer/component/userFieldVerify/index.js b/src/renderer/component/userFieldVerify/index.js deleted file mode 100644 index 12f160692..000000000 --- a/src/renderer/component/userFieldVerify/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { doUserEmailVerify, doUserPhoneVerify, doUserEmailVerifyFailure } from 'redux/actions/user'; -import { - selectEmailVerifyIsPending, - selectEmailToVerify, - selectPhoneToVerify, - selectEmailVerifyErrorMessage, - selectPhoneVerifyErrorMessage, - selectUserCountryCode, -} from 'redux/selectors/user'; -import UserFieldVerify from './view'; - -const select = state => ({ - isPending: selectEmailVerifyIsPending(state), - email: selectEmailToVerify(state), - phone: selectPhoneToVerify(state), - countryCode: selectUserCountryCode(state), - emailErrorMessage: selectEmailVerifyErrorMessage(state), - phoneErrorMessage: selectPhoneVerifyErrorMessage(state), -}); - -const perform = dispatch => ({ - verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)), - verifyUserPhone: code => dispatch(doUserPhoneVerify(code)), - verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)), -}); - -export default connect(select, perform)(UserFieldVerify); diff --git a/src/renderer/component/userPhoneNew/index.js b/src/renderer/component/userPhoneNew/index.js new file mode 100644 index 000000000..3033a3c2c --- /dev/null +++ b/src/renderer/component/userPhoneNew/index.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { doUserPhoneNew } from 'redux/actions/user'; +import { selectPhoneNewErrorMessage } from 'redux/selectors/user'; +import UserPhoneNew from './view'; + +const select = state => ({ + phoneErrorMessage: selectPhoneNewErrorMessage(state), +}); + +const perform = dispatch => ({ + addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)), +}); + +export default connect(select, perform)(UserPhoneNew); diff --git a/src/renderer/component/userFieldNew/view.jsx b/src/renderer/component/userPhoneNew/view.jsx similarity index 61% rename from src/renderer/component/userFieldNew/view.jsx rename to src/renderer/component/userPhoneNew/view.jsx index 1bcc03db6..593fd17b9 100644 --- a/src/renderer/component/userFieldNew/view.jsx +++ b/src/renderer/component/userPhoneNew/view.jsx @@ -18,13 +18,12 @@ const countryCodes = require('country-data') return 0; }); -class UserFieldNew extends React.PureComponent { +class UserPhoneNew extends React.PureComponent { constructor(props) { super(props); this.state = { phone: '', - email: '', country_code: '+1', }; @@ -50,21 +49,9 @@ class UserFieldNew extends React.PureComponent { return value; } - formatCountryCode(value) { - if (value) { - return `+${value.replace(/\D/g, '')}`; - } - return '+1'; - } - - handleChanged(event, fieldType) { - const formatter = { - email: _ => _, - phone: this.formatPhone, - country_code: this.formatCountryCode, - }; + handleChanged(event) { this.setState({ - [fieldType]: formatter[fieldType](event.target.value), + phone: this.formatPhone(event.target.value), }); } @@ -73,18 +60,14 @@ class UserFieldNew extends React.PureComponent { } handleSubmit() { - const { email, phone, country_code } = this.state; - if (phone) { - this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.substring(1)); - } else { - this.props.addUserEmail(email); - } + const { phone, country_code } = this.state; + this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.substring(1)); } render() { - const { cancelButton, emailErrorMessage, phoneErrorMessage, isPending, fieldType } = this.props; + const { cancelButton, phoneErrorMessage, isPending } = this.props; - return fieldType === 'phone' ? ( + return (

{__( @@ -107,7 +90,7 @@ class UserFieldNew extends React.PureComponent { value={this.state.phone} errorMessage={phoneErrorMessage} onChange={event => { - this.handleChanged(event, 'phone'); + this.handleChanged(event); }} />

@@ -117,32 +100,8 @@ class UserFieldNew extends React.PureComponent {
- ) : ( -
-

- {__("We'll let you know about LBRY updates, security issues, and great new content.")} -

-

{__("We'll never sell your email, and you can unsubscribe at any time.")}

-
- { - this.handleChanged(event, 'email'); - }} - /> -
- - {cancelButton} -
- -
); } } -export default UserFieldNew; +export default UserPhoneNew; diff --git a/src/renderer/component/userPhoneVerify/index.js b/src/renderer/component/userPhoneVerify/index.js new file mode 100644 index 000000000..89b18a120 --- /dev/null +++ b/src/renderer/component/userPhoneVerify/index.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { doUserPhoneVerify } from 'redux/actions/user'; +import { + selectPhoneToVerify, + selectPhoneVerifyErrorMessage, + selectUserCountryCode, +} from 'redux/selectors/user'; +import UserPhoneVerify from './view'; + +const select = state => ({ + phone: selectPhoneToVerify(state), + countryCode: selectUserCountryCode(state), + phoneErrorMessage: selectPhoneVerifyErrorMessage(state), +}); + +const perform = dispatch => ({ + verifyUserPhone: code => dispatch(doUserPhoneVerify(code)), +}); + +export default connect(select, perform)(UserPhoneVerify); diff --git a/src/renderer/component/userPhoneVerify/view.jsx b/src/renderer/component/userPhoneVerify/view.jsx new file mode 100644 index 000000000..027b18c38 --- /dev/null +++ b/src/renderer/component/userPhoneVerify/view.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import Link from 'component/link'; +import { Form, FormRow, Submit } from 'component/form.js'; + +class UserPhoneVerify extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + code: '', + }; + } + + handleCodeChanged(event) { + this.setState({ + code: String(event.target.value).trim(), + }); + } + + handleSubmit() { + const { code } = this.state; + this.props.verifyUserPhone(code); + } + + render() { + const { cancelButton, phoneErrorMessage, phone, countryCode } = this.props; + return ( +
+

+ Please enter the verification code sent to {`+${countryCode}`} + {phone}. +

+ { + this.handleCodeChanged(event); + }} + errorMessage={phoneErrorMessage} + /> + {/* render help separately so it always shows */} +
+

+ {__('Email')} or join our{' '} + {' '} + {__('if you encounter any trouble with your code.')} +

+
+
+ + {cancelButton} +
+ + ); + } +} + +export default UserPhoneVerify; diff --git a/src/renderer/modal/modalPhoneCollection/view.jsx b/src/renderer/modal/modalPhoneCollection/view.jsx index 5074387db..bf1ead9f6 100644 --- a/src/renderer/modal/modalPhoneCollection/view.jsx +++ b/src/renderer/modal/modalPhoneCollection/view.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { Modal } from 'modal/modal'; import Link from 'component/link/index'; -import UserFieldNew from 'component/userFieldNew'; -import UserFieldVerify from 'component/userFieldVerify'; +import UserPhoneNew from 'component/userPhoneNew'; +import UserPhoneVerify from 'component/userPhoneVerify'; class ModalPhoneCollection extends React.PureComponent { renderInner() { @@ -11,9 +11,9 @@ class ModalPhoneCollection extends React.PureComponent { const cancelButton = ; if (!user.phone_number && !phone) { - return ; + return ; } else if (!user.phone_number) { - return ; + return ; } closeModal(); } diff --git a/src/renderer/page/auth/view.jsx b/src/renderer/page/auth/view.jsx index 908431e26..423ce8597 100644 --- a/src/renderer/page/auth/view.jsx +++ b/src/renderer/page/auth/view.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { BusyMessage } from 'component/common'; import Link from 'component/link'; -import UserFieldNew from 'component/userFieldNew'; -import UserFieldVerify from 'component/userFieldVerify'; +import UserEmailNew from 'component/userEmailNew'; +import UserEmailVerify from 'component/userEmailVerify'; import UserVerify from 'component/userVerify'; export class AuthPage extends React.PureComponent { @@ -45,9 +45,9 @@ export class AuthPage extends React.PureComponent { if (isPending) { return [, true]; } else if (user && !user.has_verified_email && !email) { - return [, true]; + return [, true]; } else if (user && !user.has_verified_email) { - return [, true]; + return [, true]; } else if (user && !user.is_identity_verified) { return [, false]; } From e0de34d43320864f33e4aa4cfea9afd9bff07b3b Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Sun, 21 Jan 2018 23:27:29 -0800 Subject: [PATCH 18/22] Readded email collection modal and trigger at startup --- src/renderer/constants/modal_types.js | 1 + .../modal/modalEmailCollection/index.js | 21 ++++++++++ .../modal/modalEmailCollection/view.jsx | 40 +++++++++++++++++++ src/renderer/modal/modalRouter/view.jsx | 24 +++++++++-- 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/renderer/modal/modalEmailCollection/index.js create mode 100644 src/renderer/modal/modalEmailCollection/view.jsx diff --git a/src/renderer/constants/modal_types.js b/src/renderer/constants/modal_types.js index 995060647..19b86238e 100644 --- a/src/renderer/constants/modal_types.js +++ b/src/renderer/constants/modal_types.js @@ -6,6 +6,7 @@ export const ERROR = 'error'; export const INSUFFICIENT_CREDITS = 'insufficient_credits'; export const UPGRADE = 'upgrade'; export const WELCOME = 'welcome'; +export const EMAIL_COLLECTION = 'email_collection'; export const PHONE_COLLECTION = 'phone_collection'; export const FIRST_REWARD = 'first_reward'; export const AUTHENTICATION_FAILURE = 'auth_failure'; diff --git a/src/renderer/modal/modalEmailCollection/index.js b/src/renderer/modal/modalEmailCollection/index.js new file mode 100644 index 000000000..242b9e509 --- /dev/null +++ b/src/renderer/modal/modalEmailCollection/index.js @@ -0,0 +1,21 @@ +import React from 'react'; +import * as settings from 'constants/settings'; +import { connect } from 'react-redux'; +import { doCloseModal } from 'redux/actions/app'; +import { doSetClientSetting } from 'redux/actions/settings'; +import { selectEmailToVerify, selectUser } from 'redux/selectors/user'; +import ModalEmailCollection from './view'; + +const select = state => ({ + email: selectEmailToVerify(state), + user: selectUser(state), +}); + +const perform = dispatch => () => ({ + closeModal: () => { + dispatch(doSetClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED, true)); + dispatch(doCloseModal()); + }, +}); + +export default connect(select, perform)(ModalEmailCollection); diff --git a/src/renderer/modal/modalEmailCollection/view.jsx b/src/renderer/modal/modalEmailCollection/view.jsx new file mode 100644 index 000000000..a17ecf7df --- /dev/null +++ b/src/renderer/modal/modalEmailCollection/view.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Modal } from 'modal/modal'; +import Link from 'component/link/index'; +import UserEmailNew from 'component/userEmailNew'; +import UserEmailVerify from 'component/userEmailVerify'; + +class ModalEmailCollection extends React.PureComponent { + renderInner() { + const { closeModal, email, user } = this.props; + + const cancelButton = ; + + if (!user.has_verified_email && !email) { + return ; + } else if (!user.has_verified_email) { + return ; + } + closeModal(); + } + + render() { + const { user } = this.props; + + // this shouldn't happen + if (!user) { + return null; + } + + return ( + +
+

Can We Stay In Touch?

+ {this.renderInner()} +
+
+ ); + } +} + +export default ModalEmailCollection; diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx index ac254ed6d..b84272a6c 100644 --- a/src/renderer/modal/modalRouter/view.jsx +++ b/src/renderer/modal/modalRouter/view.jsx @@ -12,6 +12,7 @@ import ModalTransactionFailed from 'modal/modalTransactionFailed'; import ModalFileTimeout from 'modal/modalFileTimeout'; import ModalAffirmPurchase from 'modal/modalAffirmPurchase'; import ModalRevokeClaim from 'modal/modalRevokeClaim'; +import ModalEmailCollection from '../modalEmailCollection'; import ModalPhoneCollection from '../modalPhoneCollection'; import * as modals from 'constants/modal_types'; @@ -40,10 +41,11 @@ class ModalRouter extends React.PureComponent { return; } - const transitionModal = [this.checkShowWelcome, this.checkShowCreditIntro].reduce( - (acc, func) => (!acc ? func.bind(this)(props) : acc), - false - ); + const transitionModal = [ + this.checkShowWelcome, + this.checkShowEmail, + this.checkShowCreditIntro, + ].reduce((acc, func) => (!acc ? func.bind(this)(props) : acc), false); if ( transitionModal && @@ -64,6 +66,18 @@ class ModalRouter extends React.PureComponent { } } + checkShowEmail(props) { + const { isEmailCollectionAcknowledged, isVerificationCandidate, user } = props; + if ( + !isEmailCollectionAcknowledged && + isVerificationCandidate && + user && + !user.has_verified_email + ) { + return modals.EMAIL_COLLECTION; + } + } + checkShowCreditIntro(props) { const { balance, page, isCreditIntroAcknowledged } = props; @@ -113,6 +127,8 @@ class ModalRouter extends React.PureComponent { return ; case modals.PHONE_COLLECTION: return ; + case modals.EMAIL_COLLECTION: + return ; default: return null; } From bdc49691f05156537e1ee92ab5540137c16d343a Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Mon, 22 Jan 2018 14:06:07 -0800 Subject: [PATCH 19/22] Remove unused reducers and action types --- src/renderer/constants/action_types.js | 2 -- src/renderer/redux/reducers/user.js | 6 ------ 2 files changed, 8 deletions(-) diff --git a/src/renderer/constants/action_types.js b/src/renderer/constants/action_types.js index 6e0f40344..a72669b55 100644 --- a/src/renderer/constants/action_types.js +++ b/src/renderer/constants/action_types.js @@ -108,10 +108,8 @@ export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE'; export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED'; export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS'; export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE'; -export const USER_PHONE_DECLINE = 'USER_PHONE_DECLINE'; export const USER_PHONE_NEW_STARTED = 'USER_PHONE_NEW_STARTED'; export const USER_PHONE_NEW_SUCCESS = 'USER_PHONE_NEW_SUCCESS'; -export const USER_PHONE_NEW_EXISTS = 'USER_PHONE_NEW_EXISTS'; export const USER_PHONE_NEW_FAILURE = 'USER_PHONE_NEW_FAILURE'; export const USER_PHONE_VERIFY_STARTED = 'USER_PHONE_VERIFY_STARTED'; export const USER_PHONE_VERIFY_SUCCESS = 'USER_PHONE_VERIFY_SUCCESS'; diff --git a/src/renderer/redux/reducers/user.js b/src/renderer/redux/reducers/user.js index 3d2ea165c..c74428896 100644 --- a/src/renderer/redux/reducers/user.js +++ b/src/renderer/redux/reducers/user.js @@ -71,12 +71,6 @@ reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) => phoneNewIsPending: false, }); -reducers[ACTIONS.USER_PHONE_NEW_EXISTS] = (state, action) => - Object.assign({}, state, { - phoneToVerify: action.data.phone, - phoneNewIsPending: false, - }); - reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) => Object.assign({}, state, { phoneNewIsPending: false, From 23261d07590844cf34568be4d20d38897402ace9 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Mon, 22 Jan 2018 14:46:14 -0800 Subject: [PATCH 20/22] Removed emojis on Windows and Linux --- src/renderer/component/userPhoneNew/view.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/component/userPhoneNew/view.jsx b/src/renderer/component/userPhoneNew/view.jsx index 593fd17b9..b9999c746 100644 --- a/src/renderer/component/userPhoneNew/view.jsx +++ b/src/renderer/component/userPhoneNew/view.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { Form, FormRow, Submit } from 'component/form.js'; import FormField from 'component/formField'; +const os = require('os').type(); const countryCodes = require('country-data') .callingCountries.all.filter(_ => _.emoji) .reduce( @@ -79,7 +80,8 @@ class UserPhoneNew extends React.PureComponent { {countryCodes.map((country, index) => ( ))} From ca9f67b240c253049b83b9753c91a0ad32366e21 Mon Sep 17 00:00:00 2001 From: Liam Cardenas Date: Mon, 22 Jan 2018 15:26:34 -0800 Subject: [PATCH 21/22] Added reset button on phone verification --- src/renderer/component/userPhoneVerify/index.js | 3 ++- src/renderer/component/userPhoneVerify/view.jsx | 13 +++++++++++-- src/renderer/constants/action_types.js | 1 + src/renderer/redux/actions/user.js | 6 ++++++ src/renderer/redux/reducers/user.js | 5 +++++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/renderer/component/userPhoneVerify/index.js b/src/renderer/component/userPhoneVerify/index.js index 89b18a120..fea5e2893 100644 --- a/src/renderer/component/userPhoneVerify/index.js +++ b/src/renderer/component/userPhoneVerify/index.js @@ -1,6 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; -import { doUserPhoneVerify } from 'redux/actions/user'; +import { doUserPhoneVerify, doUserPhoneReset } from 'redux/actions/user'; import { selectPhoneToVerify, selectPhoneVerifyErrorMessage, @@ -15,6 +15,7 @@ const select = state => ({ }); const perform = dispatch => ({ + resetPhone: () => dispatch(doUserPhoneReset()), verifyUserPhone: code => dispatch(doUserPhoneVerify(code)), }); diff --git a/src/renderer/component/userPhoneVerify/view.jsx b/src/renderer/component/userPhoneVerify/view.jsx index 027b18c38..2ee39c1e8 100644 --- a/src/renderer/component/userPhoneVerify/view.jsx +++ b/src/renderer/component/userPhoneVerify/view.jsx @@ -22,13 +22,22 @@ class UserPhoneVerify extends React.PureComponent { this.props.verifyUserPhone(code); } + reset() { + const { resetPhone } = this.props; + resetPhone(); + } + render() { const { cancelButton, phoneErrorMessage, phone, countryCode } = this.props; return (

- Please enter the verification code sent to {`+${countryCode}`} - {phone}. + {__( + `Please enter the verification code sent to +${countryCode}${ + phone + }. Didn't receive it? ` + )} +

{ dispatch({ diff --git a/src/renderer/redux/reducers/user.js b/src/renderer/redux/reducers/user.js index c74428896..870eaee9e 100644 --- a/src/renderer/redux/reducers/user.js +++ b/src/renderer/redux/reducers/user.js @@ -71,6 +71,11 @@ reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) => phoneNewIsPending: false, }); +reducers[ACTIONS.USER_PHONE_RESET] = state => + Object.assign({}, state, { + phoneToVerify: null, + }); + reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) => Object.assign({}, state, { phoneNewIsPending: false, From a8258353b617098c3164f4d2a2fc1df3ce33df70 Mon Sep 17 00:00:00 2001 From: Igor Gassmann Date: Mon, 22 Jan 2018 21:28:53 -0300 Subject: [PATCH 22/22] Update yarn lock file --- yarn.lock | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yarn.lock b/yarn.lock index 7fe8e2985..53712ae80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5780,6 +5780,10 @@ modify-filename@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" +moment@^2.20.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" + move-concurrently@^1.0.1, move-concurrently@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"