mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 19:21:54 +03:00
Fix "set username" screen (#6824)
* Fix cloud-v2/#432 * Delay setting backend to local backend; don't list directory if user is not enabled * Add a way to debug specific dashboard paths * Fix bug * Check resources and status immediately
This commit is contained in:
parent
6693bdb5cd
commit
89d5b11e04
@ -140,7 +140,6 @@ const AuthContext = react.createContext<AuthContextType>({} as AuthContextType)
|
||||
/** Props for an {@link AuthProvider}. */
|
||||
export interface AuthProviderProps {
|
||||
authService: authServiceModule.AuthService
|
||||
platform: platformModule.Platform
|
||||
/** Callback to execute once the user has authenticated successfully. */
|
||||
onAuthenticated: () => void
|
||||
children: react.ReactNode
|
||||
@ -148,13 +147,12 @@ export interface AuthProviderProps {
|
||||
|
||||
/** A React provider for the Cognito API. */
|
||||
export function AuthProvider(props: AuthProviderProps) {
|
||||
const { authService, platform, children } = props
|
||||
const { authService, onAuthenticated, children } = props
|
||||
const { cognito } = authService
|
||||
const { session } = sessionProvider.useSession()
|
||||
const { setBackend } = backendProvider.useSetBackend()
|
||||
const logger = loggerProvider.useLogger()
|
||||
const navigate = router.useNavigate()
|
||||
const onAuthenticated = react.useCallback(props.onAuthenticated, [])
|
||||
const [initialized, setInitialized] = react.useState(false)
|
||||
const [userSession, setUserSession] = react.useState<UserSession | null>(null)
|
||||
|
||||
@ -174,7 +172,9 @@ export function AuthProvider(props: AuthProviderProps) {
|
||||
headers.append('Authorization', `Bearer ${accessToken}`)
|
||||
const client = new http.Client(headers)
|
||||
const backend = new remoteBackend.RemoteBackend(client, logger)
|
||||
if (platform === platformModule.Platform.cloud) {
|
||||
// The backend MUST be the remote backend before login is finished.
|
||||
// This is because the "set username" flow requires the remote backend.
|
||||
if (!initialized || userSession == null) {
|
||||
setBackend(backend)
|
||||
}
|
||||
const organization = await backend.usersMe().catch(() => null)
|
||||
@ -326,6 +326,7 @@ export function AuthProvider(props: AuthProviderProps) {
|
||||
}
|
||||
|
||||
const signOut = async () => {
|
||||
setInitialized(false)
|
||||
await cognito.signOut()
|
||||
toast.success(MESSAGES.signOutSuccess)
|
||||
return true
|
||||
@ -387,6 +388,16 @@ export function useAuth() {
|
||||
return react.useContext(AuthContext)
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// === shouldPreventNavigation ===
|
||||
// ===============================
|
||||
|
||||
/** True if navigation should be prevented, for debugging purposes. */
|
||||
function getShouldPreventNavigation() {
|
||||
const location = router.useLocation()
|
||||
return new URLSearchParams(location.search).get('prevent-navigation') === 'true'
|
||||
}
|
||||
|
||||
// =======================
|
||||
// === ProtectedLayout ===
|
||||
// =======================
|
||||
@ -394,10 +405,11 @@ export function useAuth() {
|
||||
/** A React Router layout route containing routes only accessible by users that are logged in. */
|
||||
export function ProtectedLayout() {
|
||||
const { session } = useAuth()
|
||||
const shouldPreventNavigation = getShouldPreventNavigation()
|
||||
|
||||
if (!session) {
|
||||
if (!shouldPreventNavigation && !session) {
|
||||
return <router.Navigate to={app.LOGIN_PATH} />
|
||||
} else if (session.type === UserSessionType.partial) {
|
||||
} else if (!shouldPreventNavigation && session?.type === UserSessionType.partial) {
|
||||
return <router.Navigate to={app.SET_USERNAME_PATH} />
|
||||
} else {
|
||||
return <router.Outlet context={session} />
|
||||
@ -412,8 +424,9 @@ export function ProtectedLayout() {
|
||||
* in the process of registering. */
|
||||
export function SemiProtectedLayout() {
|
||||
const { session } = useAuth()
|
||||
const shouldPreventNavigation = getShouldPreventNavigation()
|
||||
|
||||
if (session?.type === UserSessionType.full) {
|
||||
if (!shouldPreventNavigation && session?.type === UserSessionType.full) {
|
||||
return <router.Navigate to={app.DASHBOARD_PATH} />
|
||||
} else {
|
||||
return <router.Outlet context={session} />
|
||||
@ -428,10 +441,11 @@ export function SemiProtectedLayout() {
|
||||
* not logged in. */
|
||||
export function GuestLayout() {
|
||||
const { session } = useAuth()
|
||||
const shouldPreventNavigation = getShouldPreventNavigation()
|
||||
|
||||
if (session?.type === UserSessionType.partial) {
|
||||
if (!shouldPreventNavigation && session?.type === UserSessionType.partial) {
|
||||
return <router.Navigate to={app.SET_USERNAME_PATH} />
|
||||
} else if (session?.type === UserSessionType.full) {
|
||||
} else if (!shouldPreventNavigation && session?.type === UserSessionType.full) {
|
||||
return <router.Navigate to={app.DASHBOARD_PATH} />
|
||||
} else {
|
||||
return <router.Outlet />
|
||||
|
@ -39,7 +39,6 @@ import * as router from 'react-router-dom'
|
||||
import * as toast from 'react-hot-toast'
|
||||
|
||||
import * as authService from '../authentication/service'
|
||||
import * as localBackend from '../dashboard/localBackend'
|
||||
import * as platformModule from '../platform'
|
||||
|
||||
import * as authProvider from '../authentication/providers/auth'
|
||||
@ -122,8 +121,14 @@ function App(props: AppProps) {
|
||||
* because the {@link AppRouter} relies on React hooks, which can't be used in the same React
|
||||
* component as the component that defines the provider. */
|
||||
function AppRouter(props: AppProps) {
|
||||
const { logger, platform, showDashboard, onAuthenticated } = props
|
||||
const { logger, showDashboard, onAuthenticated } = props
|
||||
const navigate = router.useNavigate()
|
||||
// FIXME[sb]: After platform detection for Electron is merged in, `IS_DEV_MODE` should be
|
||||
// set to true on `ide watch`.
|
||||
if (IS_DEV_MODE) {
|
||||
// @ts-expect-error This is used exclusively for debugging.
|
||||
window.navigate = navigate
|
||||
}
|
||||
const mainPageUrl = new URL(window.location.href)
|
||||
const memoizedAuthService = react.useMemo(() => {
|
||||
const authConfig = { navigate, ...props }
|
||||
@ -164,20 +169,12 @@ function AppRouter(props: AppProps) {
|
||||
userSession={userSession}
|
||||
registerAuthEventListener={registerAuthEventListener}
|
||||
>
|
||||
<backendProvider.BackendProvider
|
||||
initialBackend={
|
||||
platform === platformModule.Platform.desktop
|
||||
? new localBackend.LocalBackend()
|
||||
: // This is UNSAFE. However, the backend will be set by the
|
||||
// authentication flow.
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
null!
|
||||
}
|
||||
>
|
||||
{/* This is safe, because the backend is always set by the authentication flow. */}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<backendProvider.BackendProvider initialBackend={null!}>
|
||||
<authProvider.AuthProvider
|
||||
authService={memoizedAuthService}
|
||||
onAuthenticated={onAuthenticated}
|
||||
platform={platform}
|
||||
>
|
||||
<modalProvider.ModalProvider>{routes}</modalProvider.ModalProvider>
|
||||
</authProvider.AuthProvider>
|
||||
|
@ -278,9 +278,17 @@ function Dashboard(props: DashboardProps) {
|
||||
backendModule.Asset<backendModule.AssetType.file>[]
|
||||
>([])
|
||||
|
||||
const canListDirectory =
|
||||
backend.platform !== platformModule.Platform.cloud || organization.isEnabled
|
||||
const directory = directoryStack[directoryStack.length - 1]
|
||||
const parentDirectory = directoryStack[directoryStack.length - 2]
|
||||
|
||||
react.useEffect(() => {
|
||||
if (platform === platformModule.Platform.desktop) {
|
||||
setBackend(new localBackend.LocalBackend())
|
||||
}
|
||||
}, [])
|
||||
|
||||
react.useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (
|
||||
@ -398,6 +406,7 @@ function Dashboard(props: DashboardProps) {
|
||||
<ProjectActionButton
|
||||
project={projectAsset}
|
||||
appRunner={appRunner}
|
||||
doRefresh={doRefresh}
|
||||
onClose={() => {
|
||||
setProject(null)
|
||||
}}
|
||||
@ -603,10 +612,14 @@ function Dashboard(props: DashboardProps) {
|
||||
hooks.useAsyncEffect(
|
||||
null,
|
||||
async signal => {
|
||||
const assets = await backend.listDirectory({ parentId: directoryId })
|
||||
if (!signal.aborted) {
|
||||
if (canListDirectory) {
|
||||
const assets = await backend.listDirectory({ parentId: directoryId })
|
||||
if (!signal.aborted) {
|
||||
setIsLoadingAssets(false)
|
||||
setAssets(assets)
|
||||
}
|
||||
} else {
|
||||
setIsLoadingAssets(false)
|
||||
setAssets(assets)
|
||||
}
|
||||
},
|
||||
[accessToken, directoryId, refresh, backend]
|
||||
@ -728,7 +741,7 @@ function Dashboard(props: DashboardProps) {
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
/>
|
||||
{backend.platform === platformModule.Platform.cloud && !organization.isEnabled ? (
|
||||
{!canListDirectory ? (
|
||||
<div className="grow grid place-items-center">
|
||||
<div className="text-base text-center">
|
||||
We will review your user details and enable the cloud experience for you
|
||||
|
@ -43,11 +43,12 @@ export interface ProjectActionButtonProps {
|
||||
appRunner: AppRunner | null
|
||||
onClose: () => void
|
||||
openIde: () => void
|
||||
doRefresh: () => void
|
||||
}
|
||||
|
||||
/** An interactive button displaying the status of a project. */
|
||||
function ProjectActionButton(props: ProjectActionButtonProps) {
|
||||
const { project, onClose, appRunner, openIde } = props
|
||||
const { project, onClose, appRunner, openIde, doRefresh } = props
|
||||
const { backend } = backendProvider.useBackend()
|
||||
|
||||
const [state, setState] = react.useState(backendModule.ProjectState.created)
|
||||
@ -101,6 +102,7 @@ function ProjectActionButton(props: ProjectActionButtonProps) {
|
||||
() => void checkProjectStatus(),
|
||||
CHECK_STATUS_INTERVAL_MS
|
||||
)
|
||||
void checkProjectStatus()
|
||||
return () => {
|
||||
clearInterval(handle)
|
||||
}
|
||||
@ -132,6 +134,7 @@ function ProjectActionButton(props: ProjectActionButtonProps) {
|
||||
() => void checkProjectResources(),
|
||||
CHECK_RESOURCES_INTERVAL_MS
|
||||
)
|
||||
void checkProjectResources()
|
||||
return () => {
|
||||
clearInterval(handle)
|
||||
}
|
||||
@ -159,10 +162,12 @@ function ProjectActionButton(props: ProjectActionButtonProps) {
|
||||
switch (backend.platform) {
|
||||
case platform.Platform.cloud:
|
||||
await backend.openProject(project.id)
|
||||
doRefresh()
|
||||
setIsCheckingStatus(true)
|
||||
break
|
||||
case platform.Platform.desktop:
|
||||
await backend.openProject(project.id)
|
||||
doRefresh()
|
||||
setState(backendModule.ProjectState.opened)
|
||||
setSpinnerState(SpinnerState.done)
|
||||
break
|
||||
|
Loading…
Reference in New Issue
Block a user