react-router#StaticContext TypeScript Examples
The following examples show how to use
react-router#StaticContext.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: AccountEnterPage.tsx From clearflask with Apache License 2.0 | 4 votes |
class AccountEnterPage extends Component<Props & WithTranslation<'site'> & RouteComponentProps<{}, StaticContext, LocationState | undefined> & ConnectProps & WithStyles<typeof styles, true>, State> {
readonly cfReturnUrl?: string;
readonly oauthFlow = new OAuthFlow({ accountType: 'admin', redirectPath: '/login' });
constructor(props) {
super(props);
this.state = {
couponId: this.props.couponId,
};
try {
const paramCfr = new URL(windowIso.location.href).searchParams.get('cfr');
if (paramCfr && new URL(paramCfr).host.endsWith(windowIso.location.host)) {
this.cfReturnUrl = paramCfr;
}
} catch (er) { }
}
async componentDidMount() {
const oauthToken = this.oauthFlow.checkResult();
if (!!oauthToken) {
this.setState({ isSubmitting: true });
var basePlanId: string | undefined;
var couponId: string | undefined;
var invitationId: string | undefined;
var redirectTo: string | undefined;
if (oauthToken?.extraData) {
try {
const oauthExtraData = JSON.parse(oauthToken?.extraData) as OauthExtraData;
couponId = oauthExtraData?.couponId;
invitationId = oauthExtraData?.invitationId;
basePlanId = oauthExtraData?.selectedPlanId;
redirectTo = oauthExtraData?.redirectTo;
} catch (e) { }
}
try {
const result = await (await ServerAdmin.get().dispatchAdmin()).accountBindAdmin({
accountBindAdmin: {
oauthToken: !oauthToken ? undefined : {
id: oauthToken.id,
code: oauthToken.code,
basePlanId,
invitationId,
couponId,
},
},
});
if (result.account) {
this.setState({
accountWasCreated: !!result.created,
redirectTo,
isSubmitting: false,
});
} else {
this.setState({ isSubmitting: false });
}
} catch (e) {
this.setState({ isSubmitting: false });
throw e;
}
} else if (this.props.type === 'coupon' || this.props.type === 'invitation') {
if (this.props.accountStatus === undefined && !this.props.account) {
ServerAdmin.get().dispatchAdmin().then(d => d.accountBindAdmin({ accountBindAdmin: {} }));
}
}
if (!!this.props.couponId) {
this.onCouponCheck(this.props.couponId);
}
}
render() {
if (this.props.plansStatus === undefined) {
ServerAdmin.get().dispatchAdmin({ debounce: true, ssr: true }).then(d => d
.plansGet());
}
const isLoggedIn = this.props.accountStatus === Status.FULFILLED && !!this.props.account;
if (this.props.type === 'invitation') {
return this.renderInvitation(isLoggedIn);
}
if (this.props.type === 'coupon') {
return this.renderCoupon(isLoggedIn);
}
if (this.props.accountStatus === Status.FULFILLED && !!this.props.account
// Only redirect once submission is over (and redirectTo and accountWasCreated is set appropriately)
&& !this.state.isSubmitting) {
if (this.props.cfJwt && this.cfReturnUrl) {
windowIso.location.href = `${this.cfReturnUrl}?${SSO_TOKEN_PARAM_NAME}=${this.props.cfJwt}`;
return (<ErrorPage msg={this.props.t('redirecting-you-back')} variant='success' />);
}
return (<RedirectIso to={this.state.redirectTo
|| this.props.location.state?.[ADMIN_LOGIN_REDIRECT_TO]
|| (this.state.accountWasCreated
? '/dashboard/welcome' :
'/dashboard')} />);
}
const selectedPlanId = this.props.location.state?.[PRE_SELECTED_BASE_PLAN_ID]
|| (this.props.plans ? this.props.plans[0].basePlanId : undefined);
if (this.props.type === 'signup') {
if (!selectedPlanId && !this.props.plans) {
return <LoadingPage />
}
if (!selectedPlanId || !SIGNUP_PROD_ENABLED && isProd() && new URL(windowIso.location.href).searchParams.get('please') !== undefined) {
return <ErrorPage variant='warning' msg={(
<div style={{ display: 'flex', alignItems: 'baseline', flexWrap: 'wrap', }} >
Direct sign ups are currently disabled. Instead,
<NavLink to='/contact/demo' className={this.props.classes.link}>schedule a demo</NavLink>
with us.
</div>
)} />
}
}
const isSingleCustomer = detectEnv() === Environment.PRODUCTION_SELF_HOST;
const isOauthEnabled = !isSingleCustomer;
const signUpOrLogIn = this.props.type === 'signup' ? this.props.t('sign-up-with') : this.props.t('log-in-with');
return (
<EnterTemplate
title={(
<>
{(this.props.type === 'signup' ? this.props.t('get-started-with') : this.props.t('welcome-back-to')) + ' '}
<span className={this.props.classes.titleClearFlask}>ClearFlask</span>
</>
)}
renderContent={submitButton => (
<>
{this.state.couponPlan && (
<Alert className={this.props.classes.alert} severity='info'>
Redeeming <span className={this.props.classes.bold}>{this.state.couponPlan.title}</span> plan.
</Alert>
)}
{this.props.invitation?.projectName && (
<Alert className={this.props.classes.alert} severity='info'>
Invitation to <span className={this.props.classes.bold}>{this.props.invitation?.projectName}</span>.
</Alert>
)}
{isOauthEnabled && (
<>
<Button
className={this.props.classes.oauthEnter}
variant='outlined'
fullWidth
size='large'
onClick={e => !!selectedPlanId && this.onOauth('google', selectedPlanId)}
disabled={this.state.isSubmitting}
>
<GoogleIcon />
{signUpOrLogIn} Google
</Button>
<Button
className={this.props.classes.oauthEnter}
variant='outlined'
fullWidth
size='large'
onClick={e => !!selectedPlanId && this.onOauth('github', selectedPlanId)}
disabled={this.state.isSubmitting}
>
<GithubIcon />
{signUpOrLogIn} GitHub
</Button>
{!isProd() && (
<Button
className={this.props.classes.oauthEnter}
variant='outlined'
fullWidth
size='large'
onClick={e => !!selectedPlanId && this.onOauth('bathtub', selectedPlanId)}
disabled={this.state.isSubmitting}
>
<BathtubIcon />
{signUpOrLogIn} Bathtub
</Button>
)}
</>
)}
<Collapse in={!this.state.useEmail}>
<Button
className={this.props.classes.oauthEnter}
variant='outlined'
fullWidth
size='large'
onClick={e => this.setState({ useEmail: true })}
disabled={this.state.isSubmitting}
>
<EmailIcon />
{signUpOrLogIn} {this.props.t('email')}
</Button>
</Collapse>
<Collapse in={this.state.useEmail}>
<div>
{isOauthEnabled && (
<Hr isInsidePaper length={120} margins={15}>{this.props.t('or')}</Hr>
)}
<Collapse in={this.props.type === 'signup'}>
<TextField
variant='outlined'
fullWidth
margin='normal'
placeholder={this.props.t('your-name-organization')}
required
value={this.state.name || ''}
onChange={e => this.setState({ name: e.target.value })}
disabled={this.state.isSubmitting}
/>
</Collapse>
<TextField
variant='outlined'
fullWidth
required
value={this.state.email || ''}
onChange={e => {
const newEmail = e.target.value;
this.setState({ email: newEmail });
if (this.props.type === 'signup') {
import(/* webpackChunkName: "emailDisposableList" */'../common/util/emailDisposableList')
.then(eu => this.setState({
emailIsFreeOrDisposable: eu.isDisposable(newEmail),
}));
}
}}
placeholder={this.props.type === 'login' ? this.props.t('email') : this.props.t('business-email')}
type='email'
margin='normal'
disabled={this.state.isSubmitting}
/>
<Collapse in={this.props.type === 'signup' && !!this.state.emailIsFreeOrDisposable}>
<Message severity='warning' message={(
<div style={{ display: 'flex', alignItems: 'baseline', flexWrap: 'wrap', }} >
{this.props.t('cannot-use-a-disposable-email')} Is this a mistake?
<NavLink to='/contact/demo' className={this.props.classes.link}>Schedule a demo</NavLink>
with us.
</div>
)} />
</Collapse>
<TextField
variant='outlined'
fullWidth
required
value={this.state.pass || ''}
onChange={e => this.setState({ pass: e.target.value })}
placeholder={this.props.t('password')}
type={this.state.revealPassword ? 'text' : 'password'}
InputProps={{
endAdornment: (
<InputAdornment position='end'>
<IconButton
aria-label='Toggle password visibility'
onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
>
{this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
</IconButton>
</InputAdornment>
)
}}
margin='normal'
disabled={this.state.isSubmitting}
/>
{this.props.type === 'signup' && (
<AcceptTerms />
)}
{submitButton}
</div>
</Collapse>
</>
)}
submitTitle={this.props.type === 'signup' ? this.props.t('create-account') : this.props.t('continue')}
submitDisabled={
!this.state.email || !this.state.pass
|| this.props.type === 'signup' && (
!this.state.name
|| !!this.state.emailIsFreeOrDisposable)}
isSubmitting={this.state.isSubmitting}
onSubmit={this.props.type === 'signup' ? this.signUp.bind(this, selectedPlanId!) : this.onLogin.bind(this)}
footer={this.props.type === 'signup' ? {
text: this.props.t('have-an-account'),
actionText: this.props.t('log-in-here'),
linkTo: {
pathname: '/login',
state: this.props.location.state,
},
} : {
text: this.props.t('no-account'),
actionText: this.props.t('sign-up-here'),
linkTo: {
pathname: '/signup',
state: this.props.location.state,
},
}}
layout={this.props.type}
/>
);
}
renderInvitation(isLoggedIn: boolean) {
// Only load invitation if we are on /invitation/...
// We want to make sure a user is not tricked into accepting an invitation
if (this.props.invitationId && this.props.invitationStatus === undefined) {
const invitationId = this.props.invitationId;
ServerAdmin.get().dispatchAdmin({ debounce: true, ssr: true, ssrStatusPassthrough: true }).then(dispatcher => dispatcher
.accountViewInvitationAdmin({ invitationId }));
}
const expired = !this.props.invitationId || this.props.invitationStatus === Status.REJECTED || (this.props.invitationStatus === Status.FULFILLED && !this.props.invitation);
const accepted = this.props.invitation?.isAcceptedByYou;
return (
<EnterTemplate
title={!!this.props.invitation ? (accepted ? (
this.props.t('invitation-accepted')
) : (
<>
{this.props.t('invitation-from')}
<span className={this.props.classes.titleClearFlask}>{this.props.invitation?.inviteeName}</span>
</>
)) : (expired ? (
<span className={this.props.classes.expired}>Invitation expired</span>
) : this.props.t('invitation-loading'))}
renderContent={submitButton => (
<>
{!!this.props.invitation ? (
<div>
{!!accepted && (
<p>You have successfully joined <span className={this.props.classes.bold}>{this.props.invitation?.inviteeName || 'friend'}</span>'s project <span className={this.props.classes.bold}>{this.props.invitation.projectName}</span> on {windowIso.parentDomain}.</p>
)}
{!accepted && (
<p>You have been invited to join the project <span className={this.props.classes.bold}>{this.props.invitation.projectName}</span> on {windowIso.parentDomain}.</p>
)}
{!isLoggedIn && (
<p>Please sign up or log in to accept this invitation.</p>
)}
</div>
) : (expired ? (
'Please let your friend know to send you another invitation by email.'
) : (
<Loading />
))}
{submitButton}
</>
)}
submitTitle={expired ? undefined : (!isLoggedIn ? this.props.t('sign-up') : (!accepted ? this.props.t('accept') : this.props.t('open')))}
submitDisabled={!this.props.invitation}
isSubmitting={this.state.isSubmitting}
onSubmit={async () => {
if (!isLoggedIn) {
this.props.history.push('/signup/', {
[ADMIN_ENTER_INVITATION_ID]: this.props.invitationId,
[ADMIN_LOGIN_REDIRECT_TO]: `/invitation/${this.props.invitationId}`,
})
} else if (!!this.props.invitationId && !!this.props.invitation?.isAcceptedByYou) {
this.props.history.push('/dashboard');
} else if (!!this.props.invitationId) {
this.setState({ isSubmitting: true });
try {
const acceptedInvitation = await (await ServerAdmin.get().dispatchAdmin()).accountAcceptInvitationAdmin({
invitationId: this.props.invitationId,
});
// Refresh projects
await (await ServerAdmin.get().dispatchAdmin()).configGetAllAndUserBindAllAdmin();
this.props.history.push(`/dashboard?projectId=${acceptedInvitation.projectId}`)
} finally {
this.setState({ isSubmitting: false });
}
}
}}
footer={!isLoggedIn && !expired ? {
text: this.props.t('have-an-account'),
actionText: this.props.t('log-in-here'),
linkTo: {
pathname: '/login',
state: {
[ADMIN_ENTER_INVITATION_ID]: this.props.invitationId,
[ADMIN_LOGIN_REDIRECT_TO]: `/invitation/${this.props.invitationId}`,
},
},
} : undefined}
layout={this.props.type}
/>
);
}
renderCoupon(isLoggedIn: boolean) {
const couponId = this.state.couponId !== undefined ? this.state.couponId : this.props.couponId || '';
return (
<EnterTemplate
title={this.props.t('redeem-coupon')}
renderContent={submitButton => (
<>
<TextField
variant='outlined'
fullWidth
margin='normal'
label={this.props.t('coupon-code')}
placeholder='XXXXXXXX'
value={couponId}
onChange={e => this.setState({ couponId: e.target.value })}
disabled={this.state.isSubmitting || !!this.state.couponPlan || !!this.state.couponRedeemedByYou}
/>
<Collapse in={!!this.state.couponRedeemedByYou}>
<Alert className={this.props.classes.alert} severity='success'>
<AlertTitle>Success!</AlertTitle>
This coupon has already been applied to your account.
</Alert>
</Collapse>
<Collapse in={!!this.state.couponPlan && !this.state.couponRedeemedByYou}>
{!!this.state.couponPlan && (
<PricingPlan plan={this.state.couponPlan} />
)}
</Collapse>
<Collapse in={!this.state.couponRedeemedByYou && !!this.state.couponPlan}>
<Alert className={this.props.classes.alert} severity='info'>
{!isLoggedIn ? 'This plan will be applied upon sign-up or login' : 'This plan will replace your current plan on your account.'}
</Alert>
</Collapse>
{submitButton}
<Collapse in={!!this.state.couponError}>
<Alert className={this.props.classes.alert} severity='warning'>
{this.state.couponError}
</Alert>
</Collapse>
</>
)}
submitTitle={!!this.state.couponRedeemedByYou
? this.props.t('continue')
: (!this.state.couponPlan
? this.props.t('check')
: (!isLoggedIn
? this.props.t('continue')
: this.props.t('redeem')))}
submitDisabled={!couponId}
isSubmitting={this.state.isSubmitting}
onSubmit={async () => {
if (!couponId) return;
if (!!this.state.couponRedeemedByYou) {
// Already redeemed by you
this.props.history.push('/dashboard');
} else if (!this.state.couponPlan) {
// Need to check couponn
await this.onCouponCheck(couponId);
} else if (!isLoggedIn) {
// Redirect to signup for a valid code
this.props.history.push('/signup/', {
[ADMIN_ENTER_COUPON_ID]: couponId,
[ADMIN_LOGIN_REDIRECT_TO]: `/coupon/${couponId}`,
})
} else {
// Accept code on own account
this.setState({ isSubmitting: true });
try {
await (await ServerAdmin.get().dispatchAdmin()).accountAcceptCouponAdmin({
couponId,
});
this.setState({
couponRedeemedByYou: true,
couponError: undefined,
});
} finally {
this.setState({ isSubmitting: false });
}
}
}}
footer={(!this.state.couponRedeemedByYou && !!this.state.couponPlan && !isLoggedIn) ? {
text: this.props.t('have-an-account'),
actionText: this.props.t('log-in-here'),
linkTo: {
pathname: '/login',
state: {
[ADMIN_ENTER_COUPON_ID]: couponId,
[ADMIN_LOGIN_REDIRECT_TO]: `/coupon/${couponId}`,
},
},
} : undefined}
layout={this.props.type}
/>
);
}
async onCouponCheck(couponId: string) {
this.setState({ isSubmitting: true });
try {
const result = await (await ServerAdmin.get().dispatchAdmin()).accountViewCouponAdmin({
couponId,
});
this.setState({
couponPlan: result.plan,
couponRedeemedByYou: !!result.redeemedByYou,
couponError: (!result.plan && !result.redeemedByYou) ? 'Coupon expired or invalid.' : undefined
});
} finally {
this.setState({ isSubmitting: false });
}
}
onOauth(type: 'google' | 'github' | 'bathtub', selectedPlanId: string) {
const extraData: OauthExtraData = {
selectedPlanId,
invitationId: this.props.location.state?.[ADMIN_ENTER_INVITATION_ID],
couponId: this.props.location.state?.[ADMIN_ENTER_COUPON_ID],
redirectTo: this.props.location.state?.[ADMIN_LOGIN_REDIRECT_TO],
};
this.oauthFlow.openForAccount(type, 'self', JSON.stringify(extraData));
}
onLogin() {
this.setState({ isSubmitting: true });
ServerAdmin.get().dispatchAdmin().then(d => d.accountLoginAdmin({
accountLogin: {
email: this.state.email || '',
password: saltHashPassword(this.state.pass || ''),
}
})).then((result) => {
this.setState({
accountWasCreated: false,
isSubmitting: false,
});
}).catch((e) => {
if (e && e.status && e.status === 403) {
this.setState({ isSubmitting: false });
} else {
this.setState({ isSubmitting: false });
}
});
}
async signUp(selectedPlanId: string) {
trackingBlock(() => {
ReactGA.event({
category: 'account-signup',
action: 'click-create',
label: selectedPlanId,
});
LinkedInTag.track('5353172');
});
this.setState({ isSubmitting: true });
const dispatchAdmin = await ServerAdmin.get().dispatchAdmin();
try {
const couponId = this.props.location.state?.[ADMIN_ENTER_COUPON_ID];
const account = await dispatchAdmin.accountSignupAdmin({
accountSignupAdmin: {
name: this.state.name!,
email: this.state.email!,
password: saltHashPassword(this.state.pass!),
basePlanId: selectedPlanId,
invitationId: this.props.location.state?.[ADMIN_ENTER_INVITATION_ID],
couponId,
}
});
const preSelectedPlanPrice = this.props.location.state?.[PRE_SELECTED_PLAN_PRICE];
if (preSelectedPlanPrice !== undefined) {
dispatchAdmin.supportMessage({
supportMessage: {
content: {
details: 'Pay what you can request',
amount: preSelectedPlanPrice,
accountId: account.accountId,
[SUPPORT_MESSAGE_FIELD_CONTACT]: account.email,
[SUPPORT_MESSAGE_FIELD_TYPE]: 'price-increase',
}
},
});
}
this.setState({
isSubmitting: false,
accountWasCreated: true,
...(!!couponId ? {
couponId,
couponRedeemedByYou: true,
} : {}),
});
} catch (e) {
this.setState({ isSubmitting: false });
throw e;
}
}
}