diff --git a/app/ide-desktop/lib/dashboard/src/components/StatelessSpinner.tsx b/app/ide-desktop/lib/dashboard/src/components/StatelessSpinner.tsx
index b0d40781fefc..61bba7f221e1 100644
--- a/app/ide-desktop/lib/dashboard/src/components/StatelessSpinner.tsx
+++ b/app/ide-desktop/lib/dashboard/src/components/StatelessSpinner.tsx
@@ -21,10 +21,14 @@ export default function StatelessSpinner(props: StatelessSpinnerProps) {
const [state, setState] = React.useState(spinner.SpinnerState.initial)
React.useEffect(() => {
- window.setTimeout(() => {
+ const timeout = window.setTimeout(() => {
setState(rawState)
})
- }, [/* should never change */ rawState])
+
+ return () => {
+ window.clearTimeout(timeout)
+ }
+ }, [rawState])
return
}
diff --git a/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx b/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx
index 3492ad93d89b..0de2ec824731 100644
--- a/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx
+++ b/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx
@@ -171,7 +171,7 @@ export default function AuthProvider(props: AuthProviderProps) {
const { children, projectManagerUrl, projectManagerRootDirectory } = props
const logger = loggerProvider.useLogger()
const { cognito } = authService ?? {}
- const { session, deinitializeSession, onSessionError } = sessionProvider.useSession()
+ const { session, onSessionError } = sessionProvider.useSession()
const { setBackendWithoutSavingType } = backendProvider.useStrictSetBackend()
const { localStorage } = localStorageProvider.useLocalStorage()
const { getText } = textProvider.useText()
@@ -628,7 +628,6 @@ export default function AuthProvider(props: AuthProviderProps) {
gtagEvent('cloud_sign_out')
cognito.saveAccessToken(null)
localStorage.clearUserSpecificEntries()
- deinitializeSession()
setInitialized(false)
sentry.setUser(null)
setUserSession(null)
diff --git a/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx b/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx
index 139ec31938ed..e344c64079a4 100644
--- a/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx
+++ b/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx
@@ -4,9 +4,6 @@ import * as React from 'react'
import * as reactQuery from '@tanstack/react-query'
-import * as asyncEffectHooks from '#/hooks/asyncEffectHooks'
-import * as refreshHooks from '#/hooks/refreshHooks'
-
import * as errorModule from '#/utilities/error'
import type * as cognito from '#/authentication/cognito'
@@ -19,8 +16,6 @@ import * as listen from '#/authentication/listen'
/** State contained in a {@link SessionContext}. */
interface SessionContextType {
readonly session: cognito.UserSession | null
- /** Set `initialized` to false. Must be called when logging out. */
- readonly deinitializeSession: () => void
readonly onSessionError: (callback: (error: Error) => void) => () => void
}
@@ -51,14 +46,14 @@ export interface SessionProviderProps {
}
const FIVE_MINUTES_MS = 300_000
+// const SIX_HOURS_MS = 21_600_000
const SIX_HOURS_MS = 21_600_000
/** A React provider for the session of the authenticated user. */
export default function SessionProvider(props: SessionProviderProps) {
const { mainPageUrl, children, userSession, registerAuthEventListener, refreshUserSession } =
props
- const [refresh, doRefresh] = refreshHooks.useRefresh()
- const [initialized, setInitialized] = React.useState(false)
+
const errorCallbacks = React.useRef(new Set<(error: Error) => void>())
/** Returns a function to unregister the listener. */
@@ -69,47 +64,40 @@ export default function SessionProvider(props: SessionProviderProps) {
}
}, [])
- // Register an async effect that will fetch the user's session whenever the `refresh` state is
- // set. This is useful when a user has just logged in (as their cached credentials are
- // out of date, so this will update them).
- const session = asyncEffectHooks.useAsyncEffect(
- null,
- async () => {
- if (userSession == null) {
- setInitialized(true)
- return null
- } else {
- try {
- const innerSession = await userSession()
- setInitialized(true)
- return innerSession
- } catch (error) {
- if (error instanceof Error) {
- for (const listener of errorCallbacks.current) {
- listener(error)
+ const queryClient = reactQuery.useQueryClient()
+
+ const session = reactQuery.useSuspenseQuery({
+ queryKey: ['userSession', userSession],
+ queryFn: userSession
+ ? () =>
+ userSession().catch(error => {
+ if (error instanceof Error) {
+ for (const listener of errorCallbacks.current) {
+ listener(error)
+ }
}
- }
- throw error
- }
- }
- },
- [refresh]
- )
+ throw error
+ })
+ : reactQuery.skipToken,
+ refetchOnWindowFocus: true,
+ refetchIntervalInBackground: true,
+ })
- const timeUntilRefresh = session
+ const timeUntilRefresh = session.data
? // If the session has not expired, we should refresh it when it is 5 minutes from expiring.
- new Date(session.expireAt).getTime() - Date.now() - FIVE_MINUTES_MS
+ new Date(session.data.expireAt).getTime() - Date.now() - FIVE_MINUTES_MS
: Infinity
+ const refreshUserSessionMutation = reactQuery.useMutation({
+ mutationKey: ['refreshUserSession', session.data],
+ mutationFn: () => refreshUserSession?.().then(() => null) ?? Promise.resolve(),
+ meta: { invalidates: [['userSession']], awaitInvalidates: true },
+ })
+
reactQuery.useQuery({
- queryKey: ['userSession'],
+ queryKey: ['refreshUserSession'],
queryFn: refreshUserSession
- ? () =>
- refreshUserSession()
- .then(() => {
- doRefresh()
- })
- .then(() => null)
+ ? () => refreshUserSessionMutation.mutateAsync()
: reactQuery.skipToken,
refetchOnWindowFocus: true,
refetchIntervalInBackground: true,
@@ -119,7 +107,6 @@ export default function SessionProvider(props: SessionProviderProps) {
// Register an effect that will listen for authentication events. When the event occurs, we
// will refresh or clear the user's session, forcing a re-render of the page with the new
// session.
- //
// For example, if a user clicks the "sign out" button, this will clear the user's session, which
// means the login screen (which is a child of this provider) should render.
React.useEffect(
@@ -128,7 +115,7 @@ export default function SessionProvider(props: SessionProviderProps) {
switch (event) {
case listen.AuthEvent.signIn:
case listen.AuthEvent.signOut: {
- doRefresh()
+ void queryClient.invalidateQueries({ queryKey: ['userSession'] })
break
}
case listen.AuthEvent.customOAuthState:
@@ -139,7 +126,7 @@ export default function SessionProvider(props: SessionProviderProps) {
// will not work.
// See https://github.com/aws-amplify/amplify-js/issues/3391#issuecomment-756473970
history.replaceState({}, '', mainPageUrl)
- doRefresh()
+ void queryClient.invalidateQueries({ queryKey: ['userSession'] })
break
}
default: {
@@ -147,16 +134,12 @@ export default function SessionProvider(props: SessionProviderProps) {
}
}
}),
- [doRefresh, registerAuthEventListener, mainPageUrl]
+ [registerAuthEventListener, mainPageUrl, queryClient]
)
- const deinitializeSession = () => {
- setInitialized(false)
- }
-
return (
-
- {initialized && children}
+
+ {children}
)
}