Skip to content

Commit be44b5b

Browse files
committed
implement saved return urls after login
1 parent a5f29fb commit be44b5b

File tree

14 files changed

+159
-75
lines changed

14 files changed

+159
-75
lines changed

www/src/components/login/CurrentUser.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import BillingPlatformPlansProvider from '../account/billing/BillingPlatformPlan
1818
import BillingSubscriptionProvider from '../account/billing/BillingSubscriptionProvider'
1919
import { PLATFORM_PLANS_QUERY } from '../account/billing/queries'
2020
import LoadingIndicator from '../utils/LoadingIndicator'
21+
import { getLoginUrlWithReturn } from 'components/users/utils'
2122

2223
export function handlePreviousUserClick({ jwt }: any) {
2324
setToken(jwt)
@@ -82,7 +83,7 @@ export function PluralProvider({ children }: any) {
8283
if (error || !data?.me?.id) {
8384
wipeToken()
8485

85-
return <Navigate to="/login" />
86+
return <Navigate to={getLoginUrlWithReturn()} />
8687
}
8788

8889
const { configuration } = data

www/src/components/overview/clusters/Clusters.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,16 @@ const PaperplaneIcon = () => (
254254
<path
255255
d="M10.2918 9.18724H7.48804"
256256
stroke="white"
257-
stroke-width="0.70095"
258-
stroke-linecap="round"
259-
stroke-linejoin="round"
257+
strokeWidth="0.70095"
258+
strokeLinecap="round"
259+
strokeLinejoin="round"
260260
/>
261261
<path
262262
d="M6.10755 13.2744C6.08291 13.3417 6.07949 13.415 6.09776 13.4843C6.11603 13.5536 6.15511 13.6156 6.20973 13.662C6.26434 13.7084 6.33188 13.737 6.40322 13.7438C6.47456 13.7506 6.54629 13.7354 6.60873 13.7002L13.9687 9.4906C14.0235 9.46029 14.0691 9.41586 14.1009 9.36193C14.1327 9.308 14.1495 9.24654 14.1495 9.18393C14.1495 9.12133 14.1327 9.05986 14.1009 9.00593C14.0691 8.952 14.0235 8.90757 13.9687 8.87727L6.60873 4.67814C6.54648 4.64332 6.47509 4.62833 6.40409 4.63516C6.33309 4.64199 6.26587 4.67031 6.2114 4.71636C6.15693 4.7624 6.1178 4.82397 6.09924 4.89284C6.08069 4.9617 6.08358 5.03459 6.10755 5.10177L7.48798 9.18787L6.10755 13.2744Z"
263263
stroke="#C5C9D3"
264-
stroke-width="0.70095"
265-
stroke-linecap="round"
266-
stroke-linejoin="round"
264+
strokeWidth="0.70095"
265+
strokeLinecap="round"
266+
strokeLinejoin="round"
267267
/>
268268
</svg>
269269
)

www/src/components/users/EmailConfirmation.tsx

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { useMutation } from '@apollo/client'
21
import { Button, Flex, SendMessageIcon, Toast } from '@pluralsh/design-system'
3-
import { Box } from 'grommet'
42
import { useCallback, useContext, useEffect, useState } from 'react'
5-
import { useParams } from 'react-router-dom'
3+
import { useNavigate, useParams } from 'react-router-dom'
64

75
import { useTheme } from 'styled-components'
86

@@ -12,16 +10,24 @@ import { Alert, AlertStatus, GqlError } from '../utils/Alert'
1210
import LoadingIndicator from '../utils/LoadingIndicator'
1311

1412
import { LoginPortal } from './LoginPortal'
15-
import { CREATE_RESET_TOKEN, REALIZE_TOKEN } from './queries'
16-
import { ResetTokenType } from './types'
13+
14+
import {
15+
ResetTokenType,
16+
useCreateResetTokenMutation,
17+
useRealizeResetTokenMutation,
18+
} from 'generated/graphql'
19+
20+
export const FROM_EMAIL_CONFIRMATION_KEY = 'fromEmailConfirmation'
1721

1822
export function EmailConfirmed() {
19-
const { id } = useParams()
20-
const [mutation, { data, error }] = useMutation(REALIZE_TOKEN, {
23+
const { id = '' } = useParams()
24+
const navigate = useNavigate()
25+
26+
const [mutation, { data, error }] = useRealizeResetTokenMutation({
2127
variables: { id, attributes: {} },
2228
onCompleted: () => {
2329
setTimeout(() => {
24-
;(window as Window).location = '/'
30+
navigate(`/account/edit?${FROM_EMAIL_CONFIRMATION_KEY}=true`)
2531
}, 2000)
2632
},
2733
})
@@ -32,7 +38,8 @@ export function EmailConfirmed() {
3238

3339
return (
3440
<LoginPortal>
35-
<Box
41+
<Flex
42+
direction="column"
3643
gap="small"
3744
width="400px"
3845
>
@@ -50,7 +57,7 @@ export function EmailConfirmed() {
5057
error={error}
5158
/>
5259
)}
53-
</Box>
60+
</Flex>
5461
</LoginPortal>
5562
)
5663
}
@@ -59,8 +66,8 @@ export function VerifyEmailConfirmed() {
5966
const theme = useTheme()
6067
const [open, setOpen] = useState(true)
6168
const me = useContext(CurrentUserContext)
62-
const [mutation] = useMutation(CREATE_RESET_TOKEN, {
63-
variables: { attributes: { email: me.email, type: ResetTokenType.EMAIL } },
69+
const [mutation] = useCreateResetTokenMutation({
70+
variables: { attributes: { email: me.email, type: ResetTokenType.Email } },
6471
onCompleted: () => setOpen(false),
6572
})
6673
const isCurrentlyOnboarding = useIsCurrentlyOnboarding()

www/src/components/users/LoginPortal.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { Flex, styledTheme } from '@pluralsh/design-system'
22

3+
import styled from 'styled-components'
34
import { Footer, FooterBalancer } from './LoginFooter'
4-
import styled, { useTheme } from 'styled-components'
5-
import { themProviderPropTypes } from 'honorable'
5+
import { Suspense } from 'react'
6+
import LoadingIndicator from 'components/utils/LoadingIndicator'
67

78
export const RIGHT_CONTENT_MAX_WIDTH = 512
89
export const RIGHT_CONTENT_PAD = styledTheme.spacing.xxlarge
@@ -13,7 +14,6 @@ export const LOGIN_BREAKPOINT = `@media screen and (min-width: ${
1314
const HERO_IMAGE_URL = '/login-bg-img.webp'
1415

1516
export function LoginPortal({ children }: any) {
16-
const { colors } = useTheme()
1717
return (
1818
<WrapperSC>
1919
<Flex
@@ -32,7 +32,7 @@ export function LoginPortal({ children }: any) {
3232
width: '100%',
3333
}}
3434
>
35-
{children}
35+
<Suspense fallback={<LoadingIndicator />}>{children}</Suspense>
3636
</div>
3737
<Footer />
3838
</Flex>

www/src/components/users/MagicLogin.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { useApolloClient } from '@apollo/client'
2+
import { Divider, LoadingSpinner } from '@pluralsh/design-system'
3+
import { Box, Collapsible, Form, Text } from 'grommet'
4+
import { A, Button, Div, Flex, Icon } from 'honorable'
5+
import queryString from 'query-string'
16
import {
27
RefObject,
38
createElement,
@@ -6,16 +11,11 @@ import {
611
useRef,
712
useState,
813
} from 'react'
9-
import { Box, Collapsible, Form, Text } from 'grommet'
10-
import { Divider, LoadingSpinner } from '@pluralsh/design-system'
11-
import { useApolloClient } from '@apollo/client'
12-
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
13-
import queryString from 'query-string'
14-
import { A, Button, Div, Flex, Icon } from 'honorable'
1514
import {
1615
GoogleReCaptchaProvider,
1716
useGoogleReCaptcha,
1817
} from 'react-google-recaptcha-v3'
18+
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
1919
import styled, { useTheme } from 'styled-components'
2020

2121
import {
@@ -31,23 +31,23 @@ import {
3131
import { WelcomeHeader } from '../utils/WelcomeHeader'
3232

3333
import { fetchToken, setToken } from '../../helpers/authentication'
34-
import { Alert, AlertStatus, GqlError } from '../utils/Alert'
35-
import { PLURAL_MARK_WHITE } from '../constants'
3634
import { host } from '../../helpers/hostname'
37-
import { useHistory } from '../../router'
35+
import { PLURAL_MARK_WHITE } from '../constants'
36+
import { Alert, AlertStatus, GqlError } from '../utils/Alert'
3837

38+
import { getLocalReturnUrl } from './utils'
3939
import { isValidEmail } from '../../utils/email'
4040

41+
import { finishedDeviceLogin } from './DeviceLoginNotif'
42+
import { LabelledInput } from './LabelledInput'
43+
import { LOGIN_BREAKPOINT, LoginPortal } from './LoginPortal'
4144
import {
4245
METHOD_ICONS,
4346
saveChallenge,
4447
saveDeviceToken,
4548
wipeChallenge,
4649
wipeDeviceToken,
4750
} from './utils'
48-
import { finishedDeviceLogin } from './DeviceLoginNotif'
49-
import { LabelledInput } from './LabelledInput'
50-
import { LOGIN_BREAKPOINT, LoginPortal } from './LoginPortal'
5151

5252
export function PasswordlessLogin() {
5353
const { token } = useParams()
@@ -120,7 +120,7 @@ export function handleOauthChallenge(client, challenge) {
120120
}
121121

122122
function LoginPoller({ challenge, token, deviceToken }: any) {
123-
const history = useHistory()
123+
const navigate = useNavigate()
124124
const client = useApolloClient()
125125
const [success, setSuccess] = useState(false)
126126

@@ -145,7 +145,7 @@ function LoginPoller({ challenge, token, deviceToken }: any) {
145145
if (challenge) {
146146
handleOauthChallenge(client, challenge)
147147
} else {
148-
history.navigate('/')
148+
navigate(getLocalReturnUrl())
149149
}
150150
}
151151
)
@@ -255,7 +255,6 @@ function LoginInternal() {
255255
const navigate = useNavigate()
256256
const [state, setState] = useState<LoginState>(State.Initial)
257257
const prevState = useRef<LoginState>(State.Initial)
258-
const history = useHistory()
259258
const client = useApolloClient()
260259
const location = useLocation()
261260
const jwt = fetchToken()
@@ -312,7 +311,7 @@ function LoginInternal() {
312311
if (challenge) {
313312
handleOauthChallenge(client, challenge)
314313
} else {
315-
history.navigate('/')
314+
navigate(getLocalReturnUrl())
316315
}
317316
},
318317
})
@@ -392,7 +391,7 @@ function LoginInternal() {
392391
setRan(true)
393392
handleOauthChallenge(client, challenge)
394393
} else if (!deviceToken && !challenge && jwt) {
395-
history.navigate('/')
394+
navigate(getLocalReturnUrl())
396395
}
397396
}, [challenge, deviceToken, history, client, jwt, ran, setRan])
398397

www/src/components/users/OAuthCallback.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect } from 'react'
22
import { useApolloClient, useMutation } from '@apollo/client'
3-
import { useLocation, useParams } from 'react-router-dom'
3+
import { useLocation, useNavigate, useParams } from 'react-router-dom'
44
import qs from 'query-string'
55
import { Box } from 'grommet'
66

@@ -13,8 +13,10 @@ import { handleOauthChallenge } from './MagicLogin'
1313
import { OAUTH_CALLBACK } from './queries'
1414
import { getChallenge, getDeviceToken } from './utils'
1515
import { finishedDeviceLogin } from './DeviceLoginNotif'
16+
import { getLocalReturnUrl } from './utils'
1617

1718
export function OAuthCallback() {
19+
const navigate = useNavigate()
1820
const location = useLocation()
1921
const client = useApolloClient()
2022
const { service } = useParams()
@@ -36,7 +38,7 @@ export function OAuthCallback() {
3638
if (challenge) {
3739
handleOauthChallenge(client, challenge)
3840
} else {
39-
window.location.href = '/'
41+
navigate(getLocalReturnUrl())
4042
}
4143
},
4244
})

www/src/components/users/SSOCallback.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Box } from 'grommet'
22
import { useEffect } from 'react'
33
import { useApolloClient, useMutation } from '@apollo/client'
4-
import { useLocation } from 'react-router-dom'
4+
import { useLocation, useNavigate } from 'react-router-dom'
55
import qs from 'query-string'
66

77
import { setToken } from '../../helpers/authentication'
@@ -12,8 +12,10 @@ import { handleOauthChallenge } from './MagicLogin'
1212
import { SSO_CALLBACK } from './queries'
1313
import { getChallenge, getDeviceToken } from './utils'
1414
import { finishedDeviceLogin } from './DeviceLoginNotif'
15+
import { getLocalReturnUrl } from './utils'
1516

1617
export function SSOCallback() {
18+
const navigate = useNavigate()
1719
const location = useLocation()
1820
const client = useApolloClient()
1921
const { code } = qs.parse(location.search)
@@ -29,7 +31,7 @@ export function SSOCallback() {
2931
if (challenge) {
3032
handleOauthChallenge(client, challenge)
3133
} else {
32-
window.location.href = '/'
34+
navigate(getLocalReturnUrl())
3335
}
3436
},
3537
})

www/src/components/users/queries.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,18 +126,6 @@ export const DELETE_CARD = gql`
126126
${AccountFragment}
127127
`
128128

129-
export const CREATE_RESET_TOKEN = gql`
130-
mutation Reset($attributes: ResetTokenAttributes!) {
131-
createResetToken(attributes: $attributes)
132-
}
133-
`
134-
135-
export const REALIZE_TOKEN = gql`
136-
mutation Realize($id: ID!, $attributes: ResetTokenRealization!) {
137-
realizeResetToken(id: $id, attributes: $attributes)
138-
}
139-
`
140-
141129
export const LIST_KEYS = gql`
142130
query Keys($cursor: String) {
143131
publicKeys(after: $cursor, first: 20) {

www/src/components/users/types.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
export const ResetTokenType = {
2-
PASSWORD: 'PASSWORD',
3-
EMAIL: 'EMAIL',
4-
}
5-
61
export const LoginMethod = {
72
PASSWORD: 'PASSWORD',
83
PASSWORDLESS: 'PASSWORDLESS',

www/src/components/users/utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const obscure = (token) => token.substring(0, 9) + 'x'.repeat(15)
88

99
const CHALLENGE_KEY = 'oauth-challenge'
1010
const DEVICE_TOKEN_KEY = 'device-token'
11+
const RETURN_TO_KEY = `return-to`
1112

1213
export const saveChallenge = (challenge) =>
1314
localStorage.setItem(CHALLENGE_KEY, challenge)
@@ -19,6 +20,27 @@ export const saveDeviceToken = (deviceToken) =>
1920
export const getDeviceToken = () => localStorage.getItem(DEVICE_TOKEN_KEY)
2021
export const wipeDeviceToken = () => localStorage.removeItem(DEVICE_TOKEN_KEY)
2122

23+
export function getLoginUrlWithReturn() {
24+
const returnPath = window.location.pathname + window.location.search
25+
if (returnPath) localStorage.setItem(RETURN_TO_KEY, returnPath)
26+
27+
return '/login'
28+
}
29+
30+
export function getLocalReturnUrl(defaultPath: string = '/'): string {
31+
const returnTo = localStorage.getItem(RETURN_TO_KEY)
32+
localStorage.removeItem(RETURN_TO_KEY)
33+
34+
if (
35+
typeof returnTo === 'string' &&
36+
returnTo.startsWith('/') &&
37+
!returnTo.startsWith('//')
38+
)
39+
return returnTo
40+
41+
return defaultPath
42+
}
43+
2244
export const METHOD_ICONS = {
2345
GOOGLE: GoogleLogoIcon,
2446
GITHUB: GitHubLogoIcon,

0 commit comments

Comments
 (0)