-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add unlink secondary login flow #15811
Changes from all commits
c0ddf22
5213996
58281b8
7ba9f6e
810b3b1
0d105e6
a7dcb51
e686df5
a7dc7c7
5cc40ba
0b2ff29
f35a043
b175e73
cda9544
1640222
1649b94
51ae018
e2f67d0
722f0d7
fafaf99
fb78f5b
cb88f68
7325dff
a850b7c
c12f968
f2333c6
93cf44c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -187,6 +187,7 @@ function beginSignIn(login) { | |
value: { | ||
...CONST.DEFAULT_ACCOUNT_DATA, | ||
isLoading: true, | ||
message: null, | ||
}, | ||
}, | ||
]; | ||
|
@@ -646,6 +647,73 @@ function authenticatePusher(socketID, channelName, callback) { | |
}); | ||
} | ||
|
||
/** | ||
* Request a new validation link / magic code to unlink an unvalidated secondary login from a primary login | ||
*/ | ||
function requestUnlinkValidationLink() { | ||
const optimisticData = [{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.ACCOUNT, | ||
value: { | ||
isLoading: true, | ||
errors: null, | ||
message: null, | ||
}, | ||
}]; | ||
const successData = [{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.ACCOUNT, | ||
value: { | ||
isLoading: false, | ||
message: Localize.translateLocal('unlinkLoginForm.linkSent'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this will return a static value, it will not translate on runtime when the use changes the language after the message is shown to the user which caused #18827 |
||
}, | ||
}]; | ||
const failureData = [{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.ACCOUNT, | ||
value: { | ||
isLoading: false, | ||
}, | ||
}]; | ||
|
||
API.write('RequestUnlinkValidationLink', {email: credentials.login}, {optimisticData, successData, failureData}); | ||
} | ||
|
||
function unlinkLogin(accountID, validateCode) { | ||
const optimisticData = [{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.ACCOUNT, | ||
value: { | ||
...CONST.DEFAULT_ACCOUNT_DATA, | ||
isLoading: true, | ||
}, | ||
}]; | ||
const successData = [{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.ACCOUNT, | ||
value: { | ||
isLoading: false, | ||
message: Localize.translateLocal('unlinkLoginForm.succesfullyUnlinkedLogin'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi ✋ Coming from #22934 We should return the message as the phrase key instead to get the dynamic message based on the language. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for highlighting this. |
||
}, | ||
}, | ||
{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.CREDENTIALS, | ||
value: { | ||
login: '', | ||
}, | ||
}]; | ||
const failureData = [{ | ||
onyxMethod: CONST.ONYX.METHOD.MERGE, | ||
key: ONYXKEYS.ACCOUNT, | ||
value: { | ||
isLoading: false, | ||
}, | ||
}]; | ||
|
||
API.write('UnlinkLogin', {accountID, validateCode}, {optimisticData, successData, failureData}); | ||
} | ||
|
||
export { | ||
beginSignIn, | ||
updatePasswordAndSignin, | ||
|
@@ -662,6 +730,8 @@ export { | |
resendLinkWithValidateCode, | ||
resetPassword, | ||
resendResetPassword, | ||
requestUnlinkValidationLink, | ||
unlinkLogin, | ||
clearSignInData, | ||
clearAccountMessages, | ||
authenticatePusher, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from 'react'; | ||
import lodashGet from 'lodash/get'; | ||
import { | ||
propTypes as validateLinkPropTypes, | ||
defaultProps as validateLinkDefaultProps, | ||
} from './ValidateLoginPage/validateLinkPropTypes'; | ||
import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; | ||
import Navigation from '../libs/Navigation/Navigation'; | ||
import * as Session from '../libs/actions/Session'; | ||
import ROUTES from '../ROUTES'; | ||
|
||
const propTypes = { | ||
/** The accountID and validateCode are passed via the URL */ | ||
route: validateLinkPropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
route: validateLinkDefaultProps, | ||
}; | ||
|
||
const UnlinkLoginPage = (props) => { | ||
const accountID = lodashGet(props.route.params, 'accountID', ''); | ||
const validateCode = lodashGet(props.route.params, 'validateCode', ''); | ||
|
||
Session.unlinkLogin(accountID, validateCode); | ||
Navigation.navigate(ROUTES.HOME); | ||
return <FullScreenLoadingIndicator />; | ||
}; | ||
|
||
UnlinkLoginPage.propTypes = propTypes; | ||
UnlinkLoginPage.defaultProps = defaultProps; | ||
UnlinkLoginPage.displayName = 'UnlinkLoginPage'; | ||
|
||
export default UnlinkLoginPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize | |
import Performance from '../../libs/Performance'; | ||
import * as App from '../../libs/actions/App'; | ||
import Permissions from '../../libs/Permissions'; | ||
import UnlinkLoginForm from './UnlinkLoginForm'; | ||
import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; | ||
import * as Localize from '../../libs/Localize'; | ||
|
||
|
@@ -29,6 +30,9 @@ const propTypes = { | |
|
||
/** Whether the account is validated */ | ||
validated: PropTypes.bool, | ||
|
||
/** The primaryLogin associated with the account */ | ||
primaryLogin: PropTypes.string, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should add a default prop for this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, why? The other values don't have a default prop associated with them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we either make the prop required else we define the default prop There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair, but neither There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think there was a lint rule that required default props or the prop to be required. Looks like it is not working lately. |
||
}), | ||
|
||
/** List of betas available to current user */ | ||
|
@@ -65,31 +69,46 @@ class SignInPage extends Component { | |
// - AND a validateCode has not been cached with sign in link | ||
const showLoginForm = !this.props.credentials.login && !this.props.credentials.validateCode; | ||
|
||
// Show the unlink form if | ||
// - A login has been entered | ||
// - AND the login is not the primary login | ||
// - AND the login is not validated | ||
const showUnlinkLoginForm = this.props.credentials.login | ||
&& this.props.account.primaryLogin | ||
&& this.props.account.primaryLogin !== this.props.credentials.login | ||
&& !this.props.account.validated; | ||
|
||
// Show the old password form if | ||
// - A login has been entered | ||
// - AND an account exists and is validated for this login | ||
// - AND a password hasn't been entered yet | ||
// - AND haven't forgotten password | ||
// - AND the login isn't an unvalidated secondary login | ||
// - AND the user is NOT on the passwordless beta | ||
const showPasswordForm = Boolean(this.props.credentials.login) | ||
&& this.props.account.validated | ||
&& !this.props.credentials.password | ||
&& !this.props.account.forgotPassword | ||
&& !showUnlinkLoginForm | ||
&& !Permissions.canUsePasswordlessLogins(this.props.betas); | ||
|
||
// Show the new magic code / validate code form if | ||
// - A login has been entered or a validateCode has been cached from sign in link | ||
// - AND the login isn't an unvalidated secondary login | ||
// - AND the user is on the 'passwordless' beta | ||
const showValidateCodeForm = (this.props.credentials.login | ||
|| this.props.credentials.validateCode) | ||
&& !showUnlinkLoginForm | ||
&& Permissions.canUsePasswordlessLogins(this.props.betas); | ||
|
||
// Show the resend validation link form if | ||
// - A login has been entered | ||
// - AND is not validated or password is forgotten | ||
// - AND the login isn't an unvalidated secondary login | ||
// - AND user is not on 'passwordless' beta | ||
const showResendValidationForm = Boolean(this.props.credentials.login) | ||
&& (!this.props.account.validated || this.props.account.forgotPassword) | ||
&& !showUnlinkLoginForm | ||
&& !Permissions.canUsePasswordlessLogins(this.props.betas); | ||
|
||
let welcomeHeader = ''; | ||
|
@@ -121,6 +140,8 @@ class SignInPage extends Component { | |
welcomeText = this.props.isSmallScreenWidth | ||
? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.enterPassword')}` | ||
: this.props.translate('welcomeText.enterPassword'); | ||
} else if (showUnlinkLoginForm) { | ||
welcomeHeader = this.props.isSmallScreenWidth ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.welcomeBack'); | ||
} else if (!showResendValidationForm) { | ||
welcomeHeader = this.props.isSmallScreenWidth ? this.props.translate('login.hero.header') : this.props.translate('welcomeText.getStarted'); | ||
welcomeText = this.props.isSmallScreenWidth ? this.props.translate('welcomeText.getStarted') : ''; | ||
|
@@ -131,8 +152,8 @@ class SignInPage extends Component { | |
<SignInPageLayout | ||
welcomeHeader={welcomeHeader} | ||
welcomeText={welcomeText} | ||
shouldShowWelcomeHeader={(showLoginForm || showPasswordForm || showValidateCodeForm || !showResendValidationForm) || !this.props.isSmallScreenWidth} | ||
shouldShowWelcomeText={showLoginForm || showPasswordForm || showValidateCodeForm || !showResendValidationForm} | ||
shouldShowWelcomeHeader={(showLoginForm || showPasswordForm || showValidateCodeForm || showUnlinkLoginForm) || !this.props.isSmallScreenWidth} | ||
shouldShowWelcomeText={showLoginForm || showPasswordForm || showValidateCodeForm} | ||
> | ||
{/* LoginForm and PasswordForm must use the isVisible prop. This keeps them mounted, but visually hidden | ||
so that password managers can access the values. Conditionally rendering these components will break this feature. */} | ||
|
@@ -143,6 +164,7 @@ class SignInPage extends Component { | |
<PasswordForm isVisible={showPasswordForm} /> | ||
)} | ||
{showResendValidationForm && <ResendValidationForm />} | ||
{showUnlinkLoginForm && <UnlinkLoginForm />} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line caused console error as New checklist item added to prevent such bugs:
|
||
</SignInPageLayout> | ||
</SafeAreaView> | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI this just got removed! And it was replaced with
Onyx.METHOD.MERGE
, whereOnyx
is imported fromreact-native-onyx
.I can spin up a fix for this real quick
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I guess this was found in this bug report: #18526