mirror of
https://github.com/enso-org/enso.git
synced 2025-01-09 02:26:47 +03:00
Fix dashboard issues (part 2) (#6511)
* Fix cloud-v2/#411 * Fix cloud-v2/#412 * Fix cloud-v2/#415 * Fix cloud-v2/#414 * Run prettier * Fix cloud-v2/#417 --------- Co-authored-by: Paweł Buchowski <pawel.buchowski@enso.org>
This commit is contained in:
parent
5b0af105c1
commit
4e7f757f53
@ -3,7 +3,9 @@
|
||||
import * as react from 'react'
|
||||
|
||||
import * as auth from '../providers/auth'
|
||||
import * as backendProvider from '../../providers/backend'
|
||||
import * as svg from '../../components/svg'
|
||||
|
||||
import Input from './input'
|
||||
import SvgIcon from './svgIcon'
|
||||
|
||||
@ -14,6 +16,7 @@ import SvgIcon from './svgIcon'
|
||||
function SetUsername() {
|
||||
const { setUsername: authSetUsername } = auth.useAuth()
|
||||
const { email } = auth.usePartialUserSession()
|
||||
const { backend } = backendProvider.useBackend()
|
||||
|
||||
const [username, setUsername] = react.useState('')
|
||||
|
||||
@ -32,7 +35,7 @@ function SetUsername() {
|
||||
<form
|
||||
onSubmit={async event => {
|
||||
event.preventDefault()
|
||||
await authSetUsername(username, email)
|
||||
await authSetUsername(backend, username, email)
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col mb-6">
|
||||
|
@ -86,7 +86,11 @@ export interface PartialUserSession {
|
||||
interface AuthContextType {
|
||||
signUp: (email: string, password: string) => Promise<boolean>
|
||||
confirmSignUp: (email: string, code: string) => Promise<boolean>
|
||||
setUsername: (username: string, email: string) => Promise<boolean>
|
||||
setUsername: (
|
||||
backend: backendProvider.AnyBackendAPI,
|
||||
username: string,
|
||||
email: string
|
||||
) => Promise<boolean>
|
||||
signInWithGoogle: () => Promise<boolean>
|
||||
signInWithGitHub: () => Promise<boolean>
|
||||
signInWithPassword: (email: string, password: string) => Promise<boolean>
|
||||
@ -166,7 +170,7 @@ export function AuthProvider(props: AuthProviderProps) {
|
||||
const client = new http.Client(headers)
|
||||
const backend = new remoteBackend.RemoteBackend(client, logger)
|
||||
setBackend(backend)
|
||||
const organization = await backend.usersMe()
|
||||
const organization = await backend.usersMe().catch(() => null)
|
||||
let newUserSession: UserSession
|
||||
if (!organization) {
|
||||
newUserSession = {
|
||||
@ -257,10 +261,14 @@ export function AuthProvider(props: AuthProviderProps) {
|
||||
return result.ok
|
||||
})
|
||||
|
||||
const setUsername = async (username: string, email: string) => {
|
||||
const { backend } = backendProvider.useBackend()
|
||||
const setUsername = async (
|
||||
backend: backendProvider.AnyBackendAPI,
|
||||
username: string,
|
||||
email: string
|
||||
) => {
|
||||
if (backend.platform === platform.Platform.desktop) {
|
||||
throw new Error('')
|
||||
toast.error('You cannot set your username on the local backend.')
|
||||
return false
|
||||
} else {
|
||||
try {
|
||||
await backend.createUser({
|
||||
@ -270,7 +278,8 @@ export function AuthProvider(props: AuthProviderProps) {
|
||||
navigate(app.DASHBOARD_PATH)
|
||||
toast.success(MESSAGES.setUsernameSuccess)
|
||||
return true
|
||||
} catch {
|
||||
} catch (e) {
|
||||
toast.error('Could not set your username.')
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -377,6 +386,22 @@ export function ProtectedLayout() {
|
||||
|
||||
if (!session) {
|
||||
return <router.Navigate to={app.LOGIN_PATH} />
|
||||
} else if (session.variant === 'partial') {
|
||||
return <router.Navigate to={app.SET_USERNAME_PATH} />
|
||||
} else {
|
||||
return <router.Outlet context={session} />
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// === SemiProtectedLayout ===
|
||||
// ===========================
|
||||
|
||||
export function SemiProtectedLayout() {
|
||||
const { session } = useAuth()
|
||||
|
||||
if (session?.variant === 'full') {
|
||||
return <router.Navigate to={app.DASHBOARD_PATH} />
|
||||
} else {
|
||||
return <router.Outlet context={session} />
|
||||
}
|
||||
|
@ -144,6 +144,9 @@ function AppRouter(props: AppProps) {
|
||||
path={DASHBOARD_PATH}
|
||||
element={showDashboard && <Dashboard {...props} />}
|
||||
/>
|
||||
</router.Route>
|
||||
{/* Semi-protected pages are visible to users currently registering. */}
|
||||
<router.Route element={<authProvider.SemiProtectedLayout />}>
|
||||
<router.Route path={SET_USERNAME_PATH} element={<SetUsername />} />
|
||||
</router.Route>
|
||||
{/* Other pages are visible to unauthenticated and authenticated users. */}
|
||||
|
@ -33,7 +33,7 @@ function ChangePasswordModal() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal className="bg-opacity-30">
|
||||
<Modal centered className="bg-opacity-30">
|
||||
<div
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
|
@ -20,8 +20,9 @@ export interface ConfirmDeleteModalProps {
|
||||
function ConfirmDeleteModal(props: ConfirmDeleteModalProps) {
|
||||
const { assetType, name, doDelete, onSuccess } = props
|
||||
const { unsetModal } = modalProvider.useSetModal()
|
||||
|
||||
return (
|
||||
<Modal className="bg-opacity-90">
|
||||
<Modal centered className="bg-opacity-90">
|
||||
<form
|
||||
className="relative bg-white shadow-soft rounded-lg w-96 p-2"
|
||||
onClick={event => {
|
||||
|
@ -31,10 +31,10 @@ function CreateForm(props: CreateFormProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal className="bg-opacity-25">
|
||||
<Modal className="absolute overflow-hidden bg-opacity-25 w-full h-full top-0 left-0">
|
||||
<form
|
||||
style={{ left, top }}
|
||||
className="absolute bg-white shadow-soft rounded-lg w-60"
|
||||
className="sticky bg-white shadow-soft rounded-lg w-60"
|
||||
onSubmit={onSubmit}
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
|
@ -525,8 +525,8 @@ function Dashboard(props: DashboardProps) {
|
||||
const CreateForm = ASSET_TYPE_CREATE_FORM[assetType]
|
||||
setModal(() => (
|
||||
<CreateForm
|
||||
left={buttonPosition.left}
|
||||
top={buttonPosition.top}
|
||||
left={buttonPosition.left + window.scrollX}
|
||||
top={buttonPosition.top + window.scrollY}
|
||||
directoryId={directoryId}
|
||||
onSuccess={doRefresh}
|
||||
/>
|
||||
@ -645,7 +645,7 @@ function Dashboard(props: DashboardProps) {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`select-none text-primary text-xs min-h-screen p-2 ${
|
||||
className={`relative select-none text-primary text-xs min-h-screen p-2 ${
|
||||
tab === Tab.dashboard ? '' : 'hidden'
|
||||
}`}
|
||||
onClick={event => {
|
||||
@ -679,20 +679,22 @@ function Dashboard(props: DashboardProps) {
|
||||
}
|
||||
}}
|
||||
setBackendPlatform={newBackendPlatform => {
|
||||
setProjectAssets([])
|
||||
setDirectoryAssets([])
|
||||
setSecretAssets([])
|
||||
setFileAssets([])
|
||||
switch (newBackendPlatform) {
|
||||
case platformModule.Platform.desktop:
|
||||
setBackend(new localBackend.LocalBackend())
|
||||
break
|
||||
case platformModule.Platform.cloud: {
|
||||
const headers = new Headers()
|
||||
headers.append('Authorization', `Bearer ${accessToken}`)
|
||||
const client = new http.Client(headers)
|
||||
setBackend(new remoteBackendModule.RemoteBackend(client, logger))
|
||||
break
|
||||
if (newBackendPlatform !== backend.platform) {
|
||||
setProjectAssets([])
|
||||
setDirectoryAssets([])
|
||||
setSecretAssets([])
|
||||
setFileAssets([])
|
||||
switch (newBackendPlatform) {
|
||||
case platformModule.Platform.desktop:
|
||||
setBackend(new localBackend.LocalBackend())
|
||||
break
|
||||
case platformModule.Platform.cloud: {
|
||||
const headers = new Headers()
|
||||
headers.append('Authorization', `Bearer ${accessToken}`)
|
||||
const client = new http.Client(headers)
|
||||
setBackend(new remoteBackendModule.RemoteBackend(client, logger))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
@ -725,6 +727,7 @@ function Dashboard(props: DashboardProps) {
|
||||
? 'opacity-50'
|
||||
: ''
|
||||
}`}
|
||||
disabled={backend.platform === platformModule.Platform.desktop}
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
setModal(() => (
|
||||
@ -738,8 +741,8 @@ function Dashboard(props: DashboardProps) {
|
||||
{svg.UPLOAD_ICON}
|
||||
</button>
|
||||
<button
|
||||
className={`mx-1 ${selectedAssets.length === 0 ? 'opacity-50' : ''}`}
|
||||
disabled={selectedAssets.length === 0}
|
||||
className={`mx-1 opacity-50`}
|
||||
disabled={true}
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
/* TODO */
|
||||
|
@ -8,17 +8,19 @@ import * as modalProvider from '../../providers/modal'
|
||||
// =================
|
||||
|
||||
export interface ModalProps extends react.PropsWithChildren {
|
||||
centered?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
function Modal(props: ModalProps) {
|
||||
const { children } = props
|
||||
const { children, centered, className } = props
|
||||
const { unsetModal } = modalProvider.useSetModal()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`fixed w-screen h-screen inset-0 bg-primary grid place-items-center ${
|
||||
props.className ?? ''
|
||||
}`}
|
||||
className={`inset-0 bg-primary ${
|
||||
centered ? 'fixed w-screen h-screen grid place-items-center ' : ''
|
||||
}${className ?? ''}`}
|
||||
onClick={event => {
|
||||
if (event.currentTarget === event.target) {
|
||||
unsetModal()
|
||||
|
@ -19,7 +19,7 @@ function RenameModal(props: RenameModalProps) {
|
||||
const { unsetModal } = modalProvider.useSetModal()
|
||||
const [newName, setNewName] = react.useState<string | null>(null)
|
||||
return (
|
||||
<Modal className="bg-opacity-90">
|
||||
<Modal centered className="bg-opacity-90">
|
||||
<form
|
||||
className="relative bg-white shadow-soft rounded-lg w-96 p-2"
|
||||
onClick={event => {
|
||||
|
@ -1,4 +1,6 @@
|
||||
/** @file The top-bar of dashboard. */
|
||||
import * as react from 'react'
|
||||
|
||||
import * as dashboard from './dashboard'
|
||||
import * as platformModule from '../../platform'
|
||||
import * as svg from '../../components/svg'
|
||||
@ -28,9 +30,18 @@ interface TopBarProps {
|
||||
*/
|
||||
function TopBar(props: TopBarProps) {
|
||||
const { platform, projectName, tab, toggleTab, setBackendPlatform, query, setQuery } = props
|
||||
const { setModal } = modalProvider.useSetModal()
|
||||
const [userMenuVisible, setUserMenuVisible] = react.useState(false)
|
||||
const { setModal, unsetModal } = modalProvider.useSetModal()
|
||||
const { backend } = backendProvider.useBackend()
|
||||
|
||||
react.useEffect(() => {
|
||||
if (userMenuVisible) {
|
||||
setModal(() => <UserMenu />)
|
||||
} else {
|
||||
unsetModal()
|
||||
}
|
||||
}, [userMenuVisible])
|
||||
|
||||
return (
|
||||
<div className="flex mb-2 h-8">
|
||||
<div className="flex text-primary">
|
||||
@ -114,7 +125,7 @@ function TopBar(props: TopBarProps) {
|
||||
className="rounded-full w-8 h-8 bg-cover cursor-pointer"
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
setModal(() => <UserMenu />)
|
||||
setUserMenuVisible(!userMenuVisible)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -46,7 +46,7 @@ function UploadFileModal(props: UploadFileModalProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal className="bg-opacity-90">
|
||||
<Modal centered className="bg-opacity-90">
|
||||
<form
|
||||
className="relative bg-white shadow-soft rounded-lg w-96 h-72 p-2"
|
||||
onClick={event => {
|
||||
|
@ -10,7 +10,7 @@ import * as remoteBackend from '../dashboard/remoteBackend'
|
||||
// =============
|
||||
|
||||
/** A type representing a backend API that may be of any type. */
|
||||
type AnyBackendAPI = localBackend.LocalBackend | remoteBackend.RemoteBackend
|
||||
export type AnyBackendAPI = localBackend.LocalBackend | remoteBackend.RemoteBackend
|
||||
|
||||
// ======================
|
||||
// === BackendContext ===
|
||||
|
Loading…
Reference in New Issue
Block a user