Skip to content

Commit

Permalink
[WebApp] Add Profile page google sso linking (#1085)
Browse files Browse the repository at this point in the history
* profile page google sso linking

* add error handling

* fix comments

* hide google sso behind variable
  • Loading branch information
rners01 authored Nov 21, 2023
1 parent 5ef181f commit d41cbd3
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 71 deletions.
2 changes: 1 addition & 1 deletion packages/web-app/src/Store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class RootStore {

constructor(axios: AxiosInstance, private readonly featureManager: FeatureManager) {
this.routing = new RouterStore()
this.auth = new AuthStore(axios, this.routing)
this.auth = new AuthStore(this, axios)
this.notifications = new NotificationStore(this)
this.xp = new ExperienceStore(axios)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const mapStoreToProps = (store: RootStore): any => ({
isMinecraftUserNameSubmitSuccess: store.profile.isMinecraftUserNameSubmitSuccess,
payPalId: store.profile.payPalId,
loadPayPalId: store.profile.loadPayPalId,
connectedGoogleAccountEmail: store.profile.connectedGoogleAccountEmail,
isLoadConnectedGoogleAccountEmailError: store.profile.isLoadConnectedGoogleAccountEmailError,
loadGoogleAccountConnection: store.profile.loadGoogleAccountConnection,
disconnectPayPalId: store.profile.disconnectPayPalId,
isPayPalIdDisconnectLoading: store.profile.isPayPalIdDisconnectLoading,
isSubmitting: store.termsAndConditions.isSubmitting,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,64 @@ import { Head } from '../../../../components'
import { withLogin } from '../../../auth-views'
import type { Avatar, Profile } from '../../../profile/models'
import { AccountTermsAndConditionsUpdate } from './AccountTermsAndConditionsUpdate'
import { GoogleSignInForm } from './GoogleSignInForm'
import { PayPalLoginButton } from './PayPalLoginButton'

const styles = (theme: SaladTheme) => ({
container: {
flex: 1,
backgroundImage: 'linear-gradient(to right, #56A431 , #AACF40)',
color: theme.darkBlue,
},
headingContainer: {
color: theme.darkBlue,
paddingBottom: '25px',
paddingBottom: 32,
},
subheadingContainer: {
color: theme.darkBlue,
paddingBottom: '7px',
paddingBottom: 40,
},
fieldContainer: {
color: theme.darkBlue,
maxWidth: '400px',
paddingBottom: '36px',
maxWidth: 400,
},
titleContainer: {
paddingBottom: 35,
},
avatarContainer: {
paddingBottom: '58px',
paddingTop: 66,
},
titleContainer: {
paddingBottom: '35px',
accountConnectionsContainer: {
paddingTop: 56,
},
payPalAccountContainer: {
color: theme.darkBlue,
maxWidth: '400px',
paddingBottom: '12px',
connectAccountDescription: {
maxWidth: 400,
paddingTop: 15,
},
accountConnectionItem: {
paddingTop: 56,
},
connectAccountButtonContainer: {
maxWidth: 400,
},
disconnectButtonContainer: {
marginTop: '12px',
marginTop: 12,
},
paypalIdContainer: {
display: 'flex',
justifyContent: 'space-between',
overflowWrap: 'anywhere',
alignItems: 'center',
marginTop: '18px',
},
connectedGoogleAccountEmail: {
maxWidth: 400,
paddingTop: 5,
wordWrap: 'break-word',
},
minecraftConnectText: {
maxWidth: 400,
paddingTop: 20,
},
connectAccountError: {
paddingTop: 10,
color: '#811417',
},
})

Expand All @@ -74,6 +92,9 @@ interface Props extends WithStyles<typeof styles> {
isMinecraftUserNameSubmitSuccess: boolean
payPalId?: string
loadPayPalId: () => void
connectedGoogleAccountEmail?: string
isLoadConnectedGoogleAccountEmailError: boolean
loadGoogleAccountConnection: () => void
disconnectPayPalId: () => void
isPayPalIdDisconnectLoading: boolean
checkPayPalId: () => void
Expand Down Expand Up @@ -104,6 +125,9 @@ class _Account extends Component<Props, State> {

public override componentDidMount() {
this.props.loadPayPalId()

// Hide google SSO until salad google account devops setup
// this.props.loadGoogleAccountConnection()
}

public override componentWillUnmount() {
Expand All @@ -130,6 +154,8 @@ class _Account extends Component<Props, State> {
isMinecraftUserNameSubmitSuccess,
payPalId,
disconnectPayPalId,
connectedGoogleAccountEmail,
isLoadConnectedGoogleAccountEmailError,
isPayPalIdDisconnectLoading,
isSubmitting,
isTermsAndConditionsAccepted,
Expand Down Expand Up @@ -158,6 +184,9 @@ class _Account extends Component<Props, State> {
}, 5000)
}

// Hide google SSO until salad google account devops setup
const hideGoogleSSO = true

return (
<div className={classes.container}>
<Scrollbars>
Expand All @@ -183,7 +212,6 @@ class _Account extends Component<Props, State> {
defaultValue={profile?.username}
/>
</div>

{avatars && (
<div className={classes.avatarContainer}>
<div className={classes.headingContainer}>
Expand All @@ -199,53 +227,99 @@ class _Account extends Component<Props, State> {
/>
</div>
)}
<div className={classes.headingContainer}>
<Text variant="baseXL">Extras</Text>
</div>
<div className={classes.fieldContainer}>
<TextField
isSubmitting={isMinecraftUserNameSubmitting}
isSubmitSuccess={isMinecraftUserNameSubmitSuccess}
validationRegexErrorMessage="Not a valid Minecraft username!"
label="Minecraft Username"
onSubmit={onUpdateMinecraftUsername}
validationRegex={/^\w{3,16}$/}
onFocus={handleSubmitButtonReset}
defaultValue={profile?.extensions?.minecraftUsername}
/>
</div>
<div className={classes.fieldContainer}>
<Text variant="baseS">
Connect Salad to your Minecraft account. A Minecraft username is required to redeem many Minecraft
rewards.
</Text>
</div>
<div className={classes.subheadingContainer}>
<Text variant="baseL">PayPal</Text>
</div>
<div className={classes.payPalAccountContainer}>
<Text variant="baseS">
Connect Salad to your PayPal account. A PayPal account is required to redeem all PayPal rewards. This
enables transfering Salad Balance to your PayPal wallet.
</Text>
</div>
<div className={classes.fieldContainer}>
{payPalId ? (
<Text variant="baseXL">
<div className={classes.paypalIdContainer}>{payPalId}</div>
<div className={classes.disconnectButtonContainer}>
<Button
onClick={disconnectPayPalId}
isLoading={isPayPalIdDisconnectLoading}
label={'Unlink PayPal Account'}
outlineColor={DefaultTheme.darkBlue}
variant={'outlined'}
/>
<div className={classes.accountConnectionsContainer}>
<Text variant="baseXL">Account Connections</Text>
<div className={classes.accountConnectionItem}>
<div className={classes.subheadingContainer}>
<Text variant="baseL">PayPal</Text>
</div>
<div className={classes.connectAccountButtonContainer}>
{payPalId ? (
<Text variant="baseXL">
<div className={classes.paypalIdContainer}>{payPalId}</div>
<div className={classes.disconnectButtonContainer}>
<Button
onClick={disconnectPayPalId}
isLoading={isPayPalIdDisconnectLoading}
label={'Unlink PayPal Account'}
outlineColor={DefaultTheme.darkBlue}
variant={'outlined'}
/>
</div>
</Text>
) : (
<>
<PayPalLoginButton onClick={handleCheckPayPalId} />
<div className={classes.connectAccountDescription}>
<Text variant="baseS">
Connect Salad to your PayPal account. A PayPal account is required to redeem all PayPal
rewards. This enables transfering Salad Balance to your PayPal wallet.
</Text>
</div>
</>
)}
</div>
</div>
{!hideGoogleSSO && (
<div className={classes.accountConnectionItem}>
<div className={classes.subheadingContainer}>
<Text variant="baseL">Google</Text>
</div>
</Text>
) : (
<PayPalLoginButton onClick={handleCheckPayPalId} />
<div className={classes.connectAccountButtonContainer}>
{connectedGoogleAccountEmail ? (
<>
<Text variant="baseS">Google Email Address</Text>
<Text variant="baseL">
<div className={classes.connectedGoogleAccountEmail}>{connectedGoogleAccountEmail}</div>
</Text>
</>
) : (
<>
<GoogleSignInForm
isTermsAndConditionsAccepted={isTermsAndConditionsAccepted}
isTermsAndConditionsRequired={shouldShowUpdateAccountTermsAndConditions}
/>
{isLoadConnectedGoogleAccountEmailError && (
<div className={classes.connectAccountError}>
<Text variant="baseS">
Unable to fetch connected Google Account. Please try to refresh the page.
</Text>
</div>
)}
<div className={classes.connectAccountDescription}>
<Text variant="baseS">
Connect Salad to your Google account. A Google account allows you to sign in easily to Salad
using Google SSO.
</Text>
</div>
</>
)}
</div>
</div>
)}
<div className={classes.accountConnectionItem}>
<div className={classes.subheadingContainer}>
<Text variant="baseL">Minecraft</Text>
</div>
<div className={classes.fieldContainer}>
<TextField
isSubmitting={isMinecraftUserNameSubmitting}
isSubmitSuccess={isMinecraftUserNameSubmitSuccess}
validationRegexErrorMessage="Not a valid Minecraft username!"
label="Minecraft Username"
onSubmit={onUpdateMinecraftUsername}
validationRegex={/^\w{3,16}$/}
onFocus={handleSubmitButtonReset}
defaultValue={profile?.extensions?.minecraftUsername}
/>
</div>
<div className={classes.minecraftConnectText}>
<Text variant="baseS">
Connect Salad to your Minecraft account. A Minecraft username is required to redeem many Minecraft
rewards.
</Text>
</div>
</div>
</div>
</Layout>
</Scrollbars>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import classNames from 'classnames'
import type { FunctionComponent } from 'react'
import type { WithStyles } from 'react-jss'
import withStyles from 'react-jss'
import { config } from '../../../../config'
import GoogleSigninDarkDisabled from './assets/GoogleSigninDarkDisabled.svg'
import GoogleSigninDarkFocused from './assets/GoogleSigninDarkFocused.svg'
import GoogleSigninDarkNormal from './assets/GoogleSigninDarkNormal.svg'
import GoogleSigninDarkPressed from './assets/GoogleSigninDarkPressed.svg'

const styles = {
formContainer: {
width: 191,
height: 46,
},
googleSigninButton: {
padding: 0,
margin: 0,
border: 'none',
width: '100%',
height: '100%',
background: 'transparent',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
cursor: 'pointer',
},
googleSigninButtonEnabled: {
backgroundImage: `url(${GoogleSigninDarkNormal})`,

'&:hover': {
backgroundImage: `url(${GoogleSigninDarkFocused})`,
},
'&:active': {
backgroundImage: `url(${GoogleSigninDarkPressed})`,
},

/*Preload google button images*/
'&::after': {
position: 'absolute',
width: 0,
height: 0,
overflow: 'hidden',
zIndex: -1,
content: `url(${GoogleSigninDarkNormal}) url(${GoogleSigninDarkDisabled}) url(${GoogleSigninDarkFocused}) url(${GoogleSigninDarkPressed})`,
},
},
googleSigninButtonDisabled: {
backgroundImage: `url(${GoogleSigninDarkDisabled})`,
cursor: 'not-allowed',
},
}

export interface GoogleSignInFormProps extends WithStyles<typeof styles> {
isTermsAndConditionsAccepted: boolean
isTermsAndConditionsRequired: boolean
}
export const _GoogleSignInForm: FunctionComponent<GoogleSignInFormProps> = ({
classes,
isTermsAndConditionsAccepted,
isTermsAndConditionsRequired,
}) => {
const shouldEnableGoogleSignInButton = !isTermsAndConditionsRequired || isTermsAndConditionsAccepted
const shouldDisableGoogleSignInButton = isTermsAndConditionsRequired && !isTermsAndConditionsAccepted

return (
<form
className={classes.formContainer}
action={`${config.apiBaseUrl}/api/v2/authentication/external`}
method="POST"
>
<input type="hidden" name="provider" value="google" />
{isTermsAndConditionsRequired && (
<input type="hidden" name="termsAccepted" value={`${isTermsAndConditionsAccepted}`} />
)}
<button
className={classNames(classes.googleSigninButton, {
[classes.googleSigninButtonEnabled]: shouldEnableGoogleSignInButton,
[classes.googleSigninButtonDisabled]: shouldDisableGoogleSignInButton,
})}
type="submit"
disabled={shouldDisableGoogleSignInButton}
/>
</form>
)
}

export const GoogleSignInForm = withStyles(styles)(_GoogleSignInForm)
Loading

0 comments on commit d41cbd3

Please sign in to comment.