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:
somebody1234 2023-05-09 18:33:11 +10:00 committed by GitHub
parent 5b0af105c1
commit 4e7f757f53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 87 additions and 39 deletions

View File

@ -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">

View File

@ -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} />
}

View File

@ -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. */}

View File

@ -33,7 +33,7 @@ function ChangePasswordModal() {
}
return (
<Modal className="bg-opacity-30">
<Modal centered className="bg-opacity-30">
<div
onClick={event => {
event.stopPropagation()

View File

@ -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 => {

View File

@ -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()

View File

@ -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 */

View File

@ -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()

View File

@ -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 => {

View File

@ -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>

View File

@ -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 => {

View File

@ -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 ===