diff --git a/app/ide-desktop/lib/dashboard/src/App.tsx b/app/ide-desktop/lib/dashboard/src/App.tsx index 280b0894807..18e4dcf85b0 100644 --- a/app/ide-desktop/lib/dashboard/src/App.tsx +++ b/app/ide-desktop/lib/dashboard/src/App.tsx @@ -42,7 +42,7 @@ import * as detect from 'enso-common/src/detect' import * as appUtils from '#/appUtils' import * as authServiceModule from '#/authentication/service' -import * as hooks from '#/hooks' +import * as navigateHooks from '#/hooks/navigateHooks' import ConfirmRegistration from '#/pages/authentication/ConfirmRegistration' import EnterOfflineMode from '#/pages/authentication/EnterOfflineMode' import ForgotPassword from '#/pages/authentication/ForgotPassword' @@ -138,7 +138,7 @@ export default function App(props: AppProps) { function AppRouter(props: AppProps) { const { logger, supportsLocalBackend, isAuthenticationDisabled, shouldShowDashboard } = props const { onAuthenticated, projectManagerUrl } = props - const navigate = hooks.useNavigate() + const navigate = navigateHooks.useNavigate() if (detect.IS_DEV_MODE) { // @ts-expect-error This is used exclusively for debugging. window.navigate = navigate diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/AssetRow.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/AssetRow.tsx index 95b07cb270f..64cacb47cc7 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/AssetRow.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/AssetRow.tsx @@ -5,7 +5,8 @@ import BlankIcon from 'enso-assets/blank.svg' import AssetEventType from '#/events/AssetEventType' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import AssetContextMenu from '#/layouts/dashboard/AssetContextMenu' import type * as assetsTable from '#/layouts/dashboard/AssetsTable' import * as authProvider from '#/providers/AuthProvider' @@ -62,7 +63,7 @@ export default function AssetRow(props: AssetRowProps) { const { organization, user } = authProvider.useNonPartialUserSession() const { backend } = backendProvider.useBackend() const { setModal, unsetModal } = modalProvider.useSetModal() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [isDraggedOver, setIsDraggedOver] = React.useState(false) const [item, setItem] = React.useState(rawItem) const dragOverTimeoutHandle = React.useRef(null) @@ -102,7 +103,7 @@ export default function AssetRow(props: AssetRowProps) { ) const copiedAsset = await backend.copyAsset( asset.id, - newParentId ?? backend.rootDirectoryId(organization), + newParentId ?? organization?.rootDirectoryId ?? backendModule.DirectoryId(''), asset.title, null ) @@ -138,7 +139,7 @@ export default function AssetRow(props: AssetRowProps) { newParentKey: backendModule.AssetId | null, newParentId: backendModule.DirectoryId | null ) => { - const rootDirectoryId = backend.rootDirectoryId(organization) + const rootDirectoryId = organization?.rootDirectoryId ?? backendModule.DirectoryId('') const nonNullNewParentKey = newParentKey ?? rootDirectoryId const nonNullNewParentId = newParentId ?? rootDirectoryId try { @@ -157,10 +158,7 @@ export default function AssetRow(props: AssetRowProps) { ) await backend.updateAsset( asset.id, - { - parentDirectoryId: newParentId ?? backend.rootDirectoryId(organization), - description: null, - }, + { parentDirectoryId: newParentId ?? rootDirectoryId, description: null }, asset.title ) } catch (error) { @@ -269,7 +267,7 @@ export default function AssetRow(props: AssetRowProps) { /* should never change */ toastAndLog, ]) - hooks.useEventHandler(assetEvents, async event => { + eventHooks.useEventHandler(assetEvents, async event => { switch (event.type) { // These events are handled in the specific `NameColumn` files. case AssetEventType.newProject: diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/DirectoryNameColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/DirectoryNameColumn.tsx index 552e6b2ce89..4ceb6216f6a 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/DirectoryNameColumn.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/DirectoryNameColumn.tsx @@ -6,7 +6,8 @@ import TriangleDownIcon from 'enso-assets/triangle_down.svg' import AssetEventType from '#/events/AssetEventType' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as backendProvider from '#/providers/BackendProvider' import * as shortcutsProvider from '#/providers/ShortcutsProvider' import * as backendModule from '#/services/backend' @@ -35,7 +36,7 @@ export default function DirectoryNameColumn(props: DirectoryNameColumnProps) { const { item, setItem, selected, setSelected, state, rowState, setRowState } = props const { numberOfSelectedItems, assetEvents, dispatchAssetListEvent, nodeMap } = state const { doToggleDirectoryExpansion } = state - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const { backend } = backendProvider.useBackend() const { shortcuts } = shortcutsProvider.useShortcuts() const asset = item.item @@ -57,7 +58,7 @@ export default function DirectoryNameColumn(props: DirectoryNameColumnProps) { } } - hooks.useEventHandler(assetEvents, async event => { + eventHooks.useEventHandler(assetEvents, async event => { switch (event.type) { case AssetEventType.newProject: case AssetEventType.uploadFiles: diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/FileNameColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/FileNameColumn.tsx index 8e49b023e06..f52aaa804e6 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/FileNameColumn.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/FileNameColumn.tsx @@ -3,7 +3,8 @@ import * as React from 'react' import AssetEventType from '#/events/AssetEventType' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as backendProvider from '#/providers/BackendProvider' import * as shortcutsProvider from '#/providers/ShortcutsProvider' import * as backendModule from '#/services/backend' @@ -32,7 +33,7 @@ export interface FileNameColumnProps extends column.AssetColumnProps {} export default function FileNameColumn(props: FileNameColumnProps) { const { item, setItem, selected, state, rowState, setRowState } = props const { assetEvents, dispatchAssetListEvent } = state - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const { backend } = backendProvider.useBackend() const { shortcuts } = shortcutsProvider.useShortcuts() const asset = item.item @@ -49,7 +50,7 @@ export default function FileNameColumn(props: FileNameColumnProps) { return await Promise.resolve(null) } - hooks.useEventHandler(assetEvents, async event => { + eventHooks.useEventHandler(assetEvents, async event => { switch (event.type) { case AssetEventType.newProject: case AssetEventType.newFolder: diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectIcon.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectIcon.tsx index 3161a20f9b8..d282f7d1e49 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectIcon.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectIcon.tsx @@ -9,7 +9,8 @@ import StopIcon from 'enso-assets/stop.svg' import type * as assetEvent from '#/events/assetEvent' import AssetEventType from '#/events/AssetEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as authProvider from '#/providers/AuthProvider' import * as backendProvider from '#/providers/BackendProvider' import * as localStorageProvider from '#/providers/LocalStorageProvider' @@ -81,7 +82,7 @@ export default function ProjectIcon(props: ProjectIconProps) { const { organization } = authProvider.useNonPartialUserSession() const { unsetModal } = modalProvider.useSetModal() const { localStorage } = localStorageProvider.useLocalStorage() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const state = item.projectState.type const setState = React.useCallback( (stateOrUpdater: React.SetStateAction) => { @@ -234,7 +235,7 @@ export default function ProjectIcon(props: ProjectIconProps) { } }, [onSpinnerStateChange]) - hooks.useEventHandler(assetEvents, event => { + eventHooks.useEventHandler(assetEvents, event => { switch (event.type) { case AssetEventType.newFolder: case AssetEventType.uploadFiles: diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx index 1f09c103e11..5630c6e0cf2 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/ProjectNameColumn.tsx @@ -5,7 +5,8 @@ import NetworkIcon from 'enso-assets/network.svg' import AssetEventType from '#/events/AssetEventType' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as authProvider from '#/providers/AuthProvider' import * as backendProvider from '#/providers/BackendProvider' import * as shortcutsProvider from '#/providers/ShortcutsProvider' @@ -39,7 +40,7 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) { const { item, setItem, selected, rowState, setRowState, state } = props const { numberOfSelectedItems, assetEvents, dispatchAssetEvent, dispatchAssetListEvent } = state const { nodeMap, doOpenManually, doOpenIde, doCloseIde } = state - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const { backend } = backendProvider.useBackend() const { organization } = authProvider.useNonPartialUserSession() const { shortcuts } = shortcutsProvider.useShortcuts() @@ -86,7 +87,7 @@ export default function ProjectNameColumn(props: ProjectNameColumnProps) { } } - hooks.useEventHandler(assetEvents, async event => { + eventHooks.useEventHandler(assetEvents, async event => { switch (event.type) { case AssetEventType.newFolder: case AssetEventType.newDataConnector: diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx index 65af5ed5219..9b0f7d3d806 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/SecretNameColumn.tsx @@ -5,7 +5,8 @@ import ConnectorIcon from 'enso-assets/connector.svg' import AssetEventType from '#/events/AssetEventType' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import UpsertSecretModal from '#/layouts/dashboard/UpsertSecretModal' import * as backendProvider from '#/providers/BackendProvider' import * as modalProvider from '#/providers/ModalProvider' @@ -34,7 +35,7 @@ export interface SecretNameColumnProps extends column.AssetColumnProps {} export default function SecretNameColumn(props: SecretNameColumnProps) { const { item, setItem, selected, state, rowState, setRowState } = props const { assetEvents, dispatchAssetListEvent } = state - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const { setModal } = modalProvider.useSetModal() const { backend } = backendProvider.useBackend() const { shortcuts } = shortcutsProvider.useShortcuts() @@ -52,7 +53,7 @@ export default function SecretNameColumn(props: SecretNameColumnProps) { await Promise.resolve(null) } - hooks.useEventHandler(assetEvents, async event => { + eventHooks.useEventHandler(assetEvents, async event => { switch (event.type) { case AssetEventType.newProject: case AssetEventType.newFolder: diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/UserPermissions.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/UserPermissions.tsx index 15e0ecc17df..ebe4b9a5878 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/UserPermissions.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/UserPermissions.tsx @@ -1,7 +1,7 @@ /** @file A user and their permissions for a specific asset. */ import * as React from 'react' -import * as hooks from '#/hooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as backendProvider from '#/providers/BackendProvider' import * as backendModule from '#/services/backend' import * as object from '#/utilities/object' @@ -24,7 +24,7 @@ export default function UserPermissions(props: UserPermissionsProps) { const { userPermission: initialUserPermission, setUserPermission: outerSetUserPermission } = props const { backend } = backendProvider.useBackend() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [userPermissions, setUserPermissions] = React.useState(initialUserPermission) React.useEffect(() => { diff --git a/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx b/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx index 696f8a25b13..01ef87e1b59 100644 --- a/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx +++ b/app/ide-desktop/lib/dashboard/src/components/dashboard/column/LabelsColumn.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import Plus2Icon from 'enso-assets/plus2.svg' -import * as hooks from '#/hooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import Category from '#/layouts/dashboard/CategorySwitcher/Category' import ManageLabelsModal from '#/layouts/dashboard/ManageLabelsModal' import * as authProvider from '#/providers/AuthProvider' @@ -36,7 +36,7 @@ export default function LabelsColumn(props: column.AssetColumnProps) { const session = authProvider.useNonPartialUserSession() const { setModal, unsetModal } = modalProvider.useSetModal() const { backend } = backendProvider.useBackend() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [isHovered, setIsHovered] = React.useState(false) const self = asset.permissions?.find( permission => permission.user.user_email === session.organization?.email diff --git a/app/ide-desktop/lib/dashboard/src/hooks/index.ts b/app/ide-desktop/lib/dashboard/src/hooks/index.ts deleted file mode 100644 index 26ed8280ae2..00000000000 --- a/app/ide-desktop/lib/dashboard/src/hooks/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** @file Barrel exports for all React Hooks, other than debug hooks. */ -export * from '#/hooks/asyncEffectHooks' -export * from '#/hooks/eventHooks' -export * from '#/hooks/navigateHooks' -export * from '#/hooks/refreshHooks' -export * from '#/hooks/routerHooks' -export * from '#/hooks/toastAndLogHooks' diff --git a/app/ide-desktop/lib/dashboard/src/hooks/routerHooks.ts b/app/ide-desktop/lib/dashboard/src/hooks/routerHooks.ts deleted file mode 100644 index 56ba046e2ef..00000000000 --- a/app/ide-desktop/lib/dashboard/src/hooks/routerHooks.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** @file Re-exports of React Router hooks. */ -import * as router from 'react-router-dom' - -/** Returns the current location object, which represents the current URL in web browsers. - * - * Note: If you're using this it may mean you're doing some of your own "routing" in your app, - * and we'd like to know what your use case is. We may be able to provide something higher-level - * to better suit your needs. - * @see {@link https://reactrouter.com/hooks/use-location} - */ -// This is a function, even though it does not look like one. -// eslint-disable-next-line no-restricted-syntax -export const useLocation = router.useLocation - -/** Returns the context (if provided) for the child route at this level of the route hierarchy. - * @see {@link https://reactrouter.com/hooks/use-outlet-context} */ -export const useOutletContext = router.useOutletContext diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx index fd482cbb91c..8cef0b5bb27 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetContextMenu.tsx @@ -5,7 +5,7 @@ import * as toast from 'react-toastify' import AssetEventType from '#/events/AssetEventType' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import type * as assetsTable from '#/layouts/dashboard/AssetsTable' import Category from '#/layouts/dashboard/CategorySwitcher/Category' import GlobalContextMenu from '#/layouts/dashboard/GlobalContextMenu' @@ -63,7 +63,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) { const { organization, accessToken } = authProvider.useNonPartialUserSession() const { setModal, unsetModal } = modalProvider.useSetModal() const { backend } = backendProvider.useBackend() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const asset = item.item const self = asset.permissions?.find( permission => permission.user.user_email === organization?.email diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSettingsPanel.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSettingsPanel.tsx index b28c49827de..2499d52b9d0 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSettingsPanel.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetSettingsPanel.tsx @@ -4,7 +4,7 @@ import * as React from 'react' import PenIcon from 'enso-assets/pen.svg' import type * as assetEvent from '#/events/assetEvent' -import * as hooks from '#/hooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import type Category from '#/layouts/dashboard/CategorySwitcher/Category' import type * as pageSwitcher from '#/layouts/dashboard/PageSwitcher' import UserBar from '#/layouts/dashboard/UserBar' @@ -55,7 +55,7 @@ export default function AssetSettingsPanel(props: AssetSettingsPanelProps) { const [description, setDescription] = React.useState('') const { organization } = authProvider.useNonPartialUserSession() const { backend } = backendProvider.useBackend() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const setItem = React.useCallback( (valueOrUpdater: React.SetStateAction) => { innerSetItem(valueOrUpdater) diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx index 624e8c7d2bb..115fd20a182 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/AssetsTable.tsx @@ -7,7 +7,9 @@ import type * as assetEvent from '#/events/assetEvent' import AssetEventType from '#/events/AssetEventType' import type * as assetListEvent from '#/events/assetListEvent' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as asyncEffectHooks from '#/hooks/asyncEffectHooks' +import * as eventHooks from '#/hooks/eventHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import type * as assetSearchBar from '#/layouts/dashboard/assetSearchBar' import type * as assetSettingsPanel from '#/layouts/dashboard/AssetSettingsPanel' import Category from '#/layouts/dashboard/CategorySwitcher/Category' @@ -354,7 +356,7 @@ export default function AssetsTable(props: AssetsTableProps) { const { setModal, unsetModal } = modalProvider.useSetModal() const { localStorage } = localStorageProvider.useLocalStorage() const { shortcuts } = shortcutsProvider.useShortcuts() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [initialized, setInitialized] = React.useState(false) const [isLoading, setIsLoading] = React.useState(true) const [extraColumns, setExtraColumns] = React.useState(() => new Set()) @@ -367,8 +369,8 @@ export default function AssetsTable(props: AssetsTableProps) { const [, setQueuedAssetEvents] = React.useState([]) const [, setNameOfProjectToImmediatelyOpen] = React.useState(initialProjectName) const rootDirectoryId = React.useMemo( - () => backend.rootDirectoryId(organization), - [backend, organization] + () => organization?.rootDirectoryId ?? backendModule.DirectoryId(''), + [organization] ) const [assetTree, setAssetTree] = React.useState(() => { const rootParentDirectoryId = backendModule.DirectoryId('') @@ -901,7 +903,7 @@ export default function AssetsTable(props: AssetsTableProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [backend, category]) - hooks.useAsyncEffect( + asyncEffectHooks.useAsyncEffect( null, async signal => { switch (backend.type) { @@ -1278,7 +1280,7 @@ export default function AssetsTable(props: AssetsTableProps) { [rootDirectoryId] ) - hooks.useEventHandler(assetListEvents, event => { + eventHooks.useEventHandler(assetListEvents, event => { switch (event.type) { case AssetListEventType.newFolder: { const siblings = nodeMapRef.current.get(event.parentKey)?.children ?? [] diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx index 7cae58af864..e37b9988624 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ChatPlaceholder.tsx @@ -6,7 +6,7 @@ import * as reactDom from 'react-dom' import CloseLargeIcon from 'enso-assets/close_large.svg' import * as appUtils from '#/appUtils' -import * as hooks from '#/hooks' +import * as navigateHooks from '#/hooks/navigateHooks' import * as chat from '#/layouts/dashboard/Chat' import * as pageSwitcher from '#/layouts/dashboard/PageSwitcher' import * as loggerProvider from '#/providers/LoggerProvider' @@ -24,7 +24,7 @@ export interface ChatPlaceholderProps { export default function ChatPlaceholder(props: ChatPlaceholderProps) { const { page, isOpen, doClose } = props const logger = loggerProvider.useLogger() - const navigate = hooks.useNavigate() + const navigate = navigateHooks.useNavigate() const [right, setTargetRight] = animations.useInterpolateOverTime( animations.interpolationFunctionEaseInOut, chat.ANIMATION_DURATION_MS, diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx index 662fcd6c684..10b69eb6a14 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Drive.tsx @@ -8,7 +8,8 @@ import type * as assetEvent from '#/events/assetEvent' import AssetEventType from '#/events/AssetEventType' import type * as assetListEvent from '#/events/assetListEvent' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as navigateHooks from '#/hooks/navigateHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import type * as assetSearchBar from '#/layouts/dashboard/assetSearchBar' import type * as assetSettingsPanel from '#/layouts/dashboard/AssetSettingsPanel' import AssetsTable from '#/layouts/dashboard/AssetsTable' @@ -77,12 +78,12 @@ export default function Drive(props: DriveProps) { const { loadingProjectManagerDidFail, isListingRemoteDirectoryWhileOffline } = props const { isListingLocalDirectoryAndWillFail, isListingRemoteDirectoryAndWillFail } = props - const navigate = hooks.useNavigate() + const navigate = navigateHooks.useNavigate() + const toastAndLog = toastAndLogHooks.useToastAndLog() const { organization } = authProvider.useNonPartialUserSession() const { backend } = backendProvider.useBackend() const { localStorage } = localStorageProvider.useLocalStorage() const { modalRef } = modalProvider.useModalRef() - const toastAndLog = hooks.useToastAndLog() const [isFileBeingDragged, setIsFileBeingDragged] = React.useState(false) const [category, setCategory] = React.useState( () => localStorage.get(localStorageModule.LocalStorageKey.driveCategory) ?? Category.home @@ -96,8 +97,8 @@ export default function Drive(props: DriveProps) { [labels] ) const rootDirectoryId = React.useMemo( - () => backend.rootDirectoryId(organization), - [backend, organization] + () => organization?.rootDirectoryId ?? backendModule.DirectoryId(''), + [organization] ) const isCloud = backend.type === backendModule.BackendType.remote diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Editor.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Editor.tsx index ba4033c4d7c..235c5c04bf2 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Editor.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/Editor.tsx @@ -1,7 +1,7 @@ /** @file The container that launches the IDE. */ import * as React from 'react' -import * as hooks from '#/hooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as backendModule from '#/services/backend' import * as load from '#/utilities/load' @@ -39,7 +39,7 @@ export interface EditorProps { /** The container that launches the IDE. */ export default function Editor(props: EditorProps) { const { hidden, supportsLocalBackend, projectStartupInfo, appRunner } = props - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [initialized, setInitialized] = React.useState(supportsLocalBackend) React.useEffect(() => { diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx index 8ee6252a603..e7fb176bf3c 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/GlobalContextMenu.tsx @@ -31,8 +31,8 @@ export default function GlobalContextMenu(props: GlobalContextMenuProps) { const { backend } = backendProvider.useBackend() const { setModal, unsetModal } = modalProvider.useSetModal() const rootDirectoryId = React.useMemo( - () => backend.rootDirectoryId(organization), - [backend, organization] + () => organization?.rootDirectoryId ?? backendModule.DirectoryId(''), + [organization] ) const filesInputRef = React.useRef(null) const isCloud = backend.type === backendModule.BackendType.remote diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManageLabelsModal.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManageLabelsModal.tsx index fdcc3e7877f..8136485f6bf 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManageLabelsModal.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManageLabelsModal.tsx @@ -1,7 +1,7 @@ /** @file A modal to select labels for an asset. */ import * as React from 'react' -import * as hooks from '#/hooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as authProvider from '#/providers/AuthProvider' import * as backendProvider from '#/providers/BackendProvider' import * as modalProvider from '#/providers/ModalProvider' @@ -39,7 +39,7 @@ export default function ManageLabelsModal< const { organization } = authProvider.useNonPartialUserSession() const { backend } = backendProvider.useBackend() const { unsetModal } = modalProvider.useSetModal() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [labels, setLabelsRaw] = React.useState(item.labels ?? []) const [query, setQuery] = React.useState('') const [color, setColor] = React.useState(null) diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManagePermissionsModal.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManagePermissionsModal.tsx index 2f03ce53fda..6a1f044be4f 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManagePermissionsModal.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/ManagePermissionsModal.tsx @@ -4,7 +4,8 @@ import * as React from 'react' import * as toast from 'react-toastify' import isEmail from 'validator/es/lib/isEmail' -import * as hooks from '#/hooks' +import * as asyncEffectHooks from '#/hooks/asyncEffectHooks' +import * as toastAndLogHooks from '#/hooks/toastAndLogHooks' import * as authProvider from '#/providers/AuthProvider' import * as backendProvider from '#/providers/BackendProvider' import * as modalProvider from '#/providers/ModalProvider' @@ -53,7 +54,7 @@ export default function ManagePermissionsModal< const { organization } = authProvider.useNonPartialUserSession() const { backend } = backendProvider.useBackend() const { unsetModal } = modalProvider.useSetModal() - const toastAndLog = hooks.useToastAndLog() + const toastAndLog = toastAndLogHooks.useToastAndLog() const [permissions, setPermissions] = React.useState(item.permissions ?? []) const [users, setUsers] = React.useState([]) const [email, setEmail] = React.useState(null) @@ -103,7 +104,7 @@ export default function ManagePermissionsModal< // This MUST be an error, otherwise the hooks below are considered as conditionally called. throw new Error('Cannot share assets on the local backend.') } else { - const listedUsers = hooks.useAsyncEffect([], () => backend.listUsers(), []) + const listedUsers = asyncEffectHooks.useAsyncEffect([], () => backend.listUsers(), []) const allUsers = React.useMemo( () => listedUsers.filter( diff --git a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx index 3b246d0a634..b1281a70d68 100644 --- a/app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx +++ b/app/ide-desktop/lib/dashboard/src/layouts/dashboard/TopBar.tsx @@ -56,7 +56,9 @@ export default function TopBar(props: TopBarProps) { {supportsLocalBackend && page !== pageSwitcher.Page.editor && ( )} - {page !== pageSwitcher.Page.editor && ( + {page === pageSwitcher.Page.editor ? ( +
+ ) : (
{ - await Promise.resolve(null) - } - - hooks.useEventHandler(assetEvents, async event => { - switch (event.type) { - case AssetEventType.newProject: - case AssetEventType.newFolder: - case AssetEventType.uploadFiles: - case AssetEventType.openProject: - case AssetEventType.closeProject: - case AssetEventType.cancelOpeningAllProjects: - case AssetEventType.copy: - case AssetEventType.cut: - case AssetEventType.cancelCut: - case AssetEventType.move: - case AssetEventType.delete: - case AssetEventType.restore: - case AssetEventType.download: - case AssetEventType.downloadSelected: - case AssetEventType.removeSelf: - case AssetEventType.temporarilyAddLabels: - case AssetEventType.temporarilyRemoveLabels: - case AssetEventType.addLabels: - case AssetEventType.removeLabels: - case AssetEventType.deleteLabel: { - // Ignored. These events should all be unrelated to secrets. - // `deleteMultiple`, `restoreMultiple`, `download`, - // and `downloadSelected` are handled by `AssetRow`. - break - } - case AssetEventType.newDataConnector: { - if (item.key === event.placeholderId) { - if (backend.type !== backendModule.BackendType.remote) { - toastAndLog('Data connectors cannot be created on the local backend') - } else { - rowState.setVisibility(Visibility.faded) - try { - const id = await backend.createSecret({ - parentDirectoryId: asset.parentId, - name: asset.title, - value: event.value, - }) - rowState.setVisibility(Visibility.visible) - setAsset(object.merger({ id })) - } catch (error) { - dispatchAssetListEvent({ - type: AssetListEventType.delete, - key: item.key, - }) - toastAndLog('Error creating new data connector', error) - } - } - } - break - } - } - }) - - return ( -
{ - if (rowState.isEditingName && event.key === 'Enter') { - event.stopPropagation() - } - }} - onClick={event => { - if ( - eventModule.isSingleClick(event) && - (selected || - shortcuts.matchesMouseAction(shortcutsModule.MouseAction.editName, event)) - ) { - setRowState(object.merger({ isEditingName: true })) - } else if (eventModule.isDoubleClick(event)) { - event.stopPropagation() - setModal( - { - try { - await backend.updateSecret(asset.id, { value }, asset.title) - } catch (error) { - toastAndLog(null, error) - } - }} - /> - ) - } - }} - > - - { - setRowState(object.merger({ isEditingName: false })) - if (newTitle !== asset.title) { - const oldTitle = asset.title - setAsset(object.merger({ title: newTitle })) - try { - await doRename() - } catch { - setAsset(object.merger({ title: oldTitle })) - } - } - }} - onCancel={() => { - setRowState(object.merger({ isEditingName: false })) - }} - className="bg-transparent grow leading-170 h-6 py-px" - > - {asset.title} - -
- ) -} diff --git a/app/ide-desktop/lib/dashboard/src/pages/authentication/ConfirmRegistration.tsx b/app/ide-desktop/lib/dashboard/src/pages/authentication/ConfirmRegistration.tsx index 73c44a8dab0..ce4f6e64230 100644 --- a/app/ide-desktop/lib/dashboard/src/pages/authentication/ConfirmRegistration.tsx +++ b/app/ide-desktop/lib/dashboard/src/pages/authentication/ConfirmRegistration.tsx @@ -2,10 +2,11 @@ * email address. */ import * as React from 'react' +import * as router from 'react-router-dom' import * as toastify from 'react-toastify' import * as appUtils from '#/appUtils' -import * as hooks from '#/hooks' +import * as navigateHooks from '#/hooks/navigateHooks' import * as authProvider from '#/providers/AuthProvider' import * as loggerProvider from '#/providers/LoggerProvider' @@ -27,8 +28,8 @@ const REGISTRATION_QUERY_PARAMS = { export default function ConfirmRegistration() { const logger = loggerProvider.useLogger() const auth = authProvider.useAuth() - const location = hooks.useLocation() - const navigate = hooks.useNavigate() + const location = router.useLocation() + const navigate = navigateHooks.useNavigate() const { verificationCode, email, redirectUrl } = parseUrlSearchParams(location.search) diff --git a/app/ide-desktop/lib/dashboard/src/pages/authentication/EnterOfflineMode.tsx b/app/ide-desktop/lib/dashboard/src/pages/authentication/EnterOfflineMode.tsx index 9b3c0b1be52..832df1144d7 100644 --- a/app/ide-desktop/lib/dashboard/src/pages/authentication/EnterOfflineMode.tsx +++ b/app/ide-desktop/lib/dashboard/src/pages/authentication/EnterOfflineMode.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as appUtils from '#/appUtils' -import * as hooks from '#/hooks' +import * as navigateHooks from '#/hooks/navigateHooks' import * as authProvider from '#/providers/AuthProvider' // ======================== @@ -12,7 +12,7 @@ import * as authProvider from '#/providers/AuthProvider' /** An empty component redirecting users based on the backend response to user registration. */ export default function EnterOfflineMode() { const { goOffline } = authProvider.useAuth() - const navigate = hooks.useNavigate() + const navigate = navigateHooks.useNavigate() React.useEffect(() => { void (async () => { diff --git a/app/ide-desktop/lib/dashboard/src/pages/authentication/ResetPassword.tsx b/app/ide-desktop/lib/dashboard/src/pages/authentication/ResetPassword.tsx index 727b818f365..64a290647f0 100644 --- a/app/ide-desktop/lib/dashboard/src/pages/authentication/ResetPassword.tsx +++ b/app/ide-desktop/lib/dashboard/src/pages/authentication/ResetPassword.tsx @@ -10,7 +10,7 @@ import GoBackIcon from 'enso-assets/go_back.svg' import LockIcon from 'enso-assets/lock.svg' import * as appUtils from '#/appUtils' -import * as hooks from '#/hooks' +import * as navigateHooks from '#/hooks/navigateHooks' import * as authProvider from '#/providers/AuthProvider' import * as string from '#/utilities/string' import * as validation from '#/utilities/validation' @@ -36,7 +36,7 @@ const RESET_PASSWORD_QUERY_PARAMS = { export default function ResetPassword() { const { resetPassword } = authProvider.useAuth() const { search } = router.useLocation() - const navigate = hooks.useNavigate() + const navigate = navigateHooks.useNavigate() const { verificationCode, email } = parseUrlSearchParams(search) diff --git a/app/ide-desktop/lib/dashboard/src/pages/dashboard/Dashboard.tsx b/app/ide-desktop/lib/dashboard/src/pages/dashboard/Dashboard.tsx index 10182c2837e..0f101d90af1 100644 --- a/app/ide-desktop/lib/dashboard/src/pages/dashboard/Dashboard.tsx +++ b/app/ide-desktop/lib/dashboard/src/pages/dashboard/Dashboard.tsx @@ -6,7 +6,7 @@ import type * as assetEvent from '#/events/assetEvent' import AssetEventType from '#/events/AssetEventType' import type * as assetListEvent from '#/events/assetListEvent' import AssetListEventType from '#/events/AssetListEventType' -import * as hooks from '#/hooks' +import * as eventHooks from '#/hooks/eventHooks' import type * as assetSearchBar from '#/layouts/dashboard/assetSearchBar' import type * as assetSettingsPanel from '#/layouts/dashboard/AssetSettingsPanel' import AssetSettingsPanel from '#/layouts/dashboard/AssetSettingsPanel' @@ -78,8 +78,8 @@ export default function Dashboard(props: DashboardProps) { const [openProjectAbortController, setOpenProjectAbortController] = React.useState(null) const [assetListEvents, dispatchAssetListEvent] = - hooks.useEvent() - const [assetEvents, dispatchAssetEvent] = hooks.useEvent() + eventHooks.useEvent() + const [assetEvents, dispatchAssetEvent] = eventHooks.useEvent() const [assetSettingsPanelProps, setAssetSettingsPanelProps] = React.useState(null) const [isAssetSettingsPanelVisible, setIsAssetSettingsPanelVisible] = React.useState( @@ -89,8 +89,8 @@ export default function Dashboard(props: DashboardProps) { ) const [initialProjectName, setInitialProjectName] = React.useState(rawInitialProjectName) const rootDirectoryId = React.useMemo( - () => backend.rootDirectoryId(session.organization), - [backend, session.organization] + () => session.organization?.rootDirectoryId ?? backendModule.DirectoryId(''), + [session.organization] ) const isListingLocalDirectoryAndWillFail = @@ -142,7 +142,7 @@ export default function Dashboard(props: DashboardProps) { if ( currentBackend.type === backendModule.BackendType.remote && savedProjectStartupInfo.projectAsset.parentId === - backend.rootDirectoryId(session.organization) + session.organization.rootDirectoryId ) { // `projectStartupInfo` is still `null`, so the `editor` page will be empty. setPage(pageSwitcher.Page.drive) @@ -220,7 +220,7 @@ export default function Dashboard(props: DashboardProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - hooks.useEventHandler(assetEvents, event => { + eventHooks.useEventHandler(assetEvents, event => { switch (event.type) { case AssetEventType.openProject: { openProjectAbortController?.abort() diff --git a/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx b/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx index c8ac2f5cc66..40780d9ebb4 100644 --- a/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx +++ b/app/ide-desktop/lib/dashboard/src/providers/AuthProvider.tsx @@ -14,7 +14,6 @@ import * as gtag from 'enso-common/src/gtag' import * as appUtils from '#/appUtils' import * as cognitoModule from '#/authentication/cognito' import type * as authServiceModule from '#/authentication/service' -import * as hooks from '#/hooks' import LoadingScreen from '#/pages/authentication/LoadingScreen' import * as backendProvider from '#/providers/BackendProvider' import * as localStorageProvider from '#/providers/LocalStorageProvider' @@ -692,7 +691,7 @@ export function GuestLayout() { /** A React context hook returning the user session * for a user that has not yet completed registration. */ export function usePartialUserSession() { - return hooks.useOutletContext() + return router.useOutletContext() } // ================================ @@ -701,5 +700,5 @@ export function usePartialUserSession() { /** A React context hook returning the user session for a user that can perform actions. */ export function useNonPartialUserSession() { - return hooks.useOutletContext>() + return router.useOutletContext>() } diff --git a/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx b/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx index 3e154de7f5f..85a11999974 100644 --- a/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx +++ b/app/ide-desktop/lib/dashboard/src/providers/SessionProvider.tsx @@ -4,7 +4,8 @@ import * as React from 'react' import type * as cognito from '#/authentication/cognito' import * as listen from '#/authentication/listen' -import * as hooks from '#/hooks' +import * as asyncEffectHooks from '#/hooks/asyncEffectHooks' +import * as refreshHooks from '#/hooks/refreshHooks' import * as error from '#/utilities/error' // ====================== @@ -51,7 +52,7 @@ export interface SessionProviderProps { export default function SessionProvider(props: SessionProviderProps) { const { mainPageUrl, children, userSession, registerAuthEventListener } = props - const [refresh, doRefresh] = hooks.useRefresh() + const [refresh, doRefresh] = refreshHooks.useRefresh() /** Flag used to avoid rendering child components until we've fetched the user's session at least * once. Avoids flash of the login screen when the user is already logged in. */ @@ -60,7 +61,7 @@ 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 = hooks.useAsyncEffect( + const session = asyncEffectHooks.useAsyncEffect( null, async () => { const innerSession = await userSession() diff --git a/app/ide-desktop/lib/dashboard/src/services/backend.ts b/app/ide-desktop/lib/dashboard/src/services/backend.ts index aaaedfbe3fc..dcfe677d197 100644 --- a/app/ide-desktop/lib/dashboard/src/services/backend.ts +++ b/app/ide-desktop/lib/dashboard/src/services/backend.ts @@ -93,6 +93,7 @@ export interface UserOrOrganization { /** If `false`, this account is awaiting acceptance from an admin, and endpoints other than * `usersMe` will not work. */ isEnabled: boolean + rootDirectoryId: DirectoryId } /** A `Directory` returned by `createDirectory`. */ @@ -835,8 +836,6 @@ export function stripProjectExtension(name: string) { export abstract class Backend { abstract readonly type: BackendType - /** Return the root directory id for the given user. */ - abstract rootDirectoryId(user: UserOrOrganization | null): DirectoryId /** Return a list of all users in the same organization. */ abstract listUsers(): Promise /** Set the username of the current user. */ diff --git a/app/ide-desktop/lib/dashboard/src/services/localBackend.ts b/app/ide-desktop/lib/dashboard/src/services/localBackend.ts index 25075cc2484..d49c5c86c7a 100644 --- a/app/ide-desktop/lib/dashboard/src/services/localBackend.ts +++ b/app/ide-desktop/lib/dashboard/src/services/localBackend.ts @@ -42,10 +42,6 @@ export class LocalBackend extends backend.Backend { } } - /** Return the root directory id for the given user. */ - override rootDirectoryId(): backend.DirectoryId { - return backend.DirectoryId('') - } /** Return a list of assets in a directory. * @throws An error if the JSON-RPC call fails. */ override async listDirectory(): Promise { diff --git a/app/ide-desktop/lib/dashboard/src/services/remoteBackend.ts b/app/ide-desktop/lib/dashboard/src/services/remoteBackend.ts index ab8e6c60708..3fb86a62a3f 100644 --- a/app/ide-desktop/lib/dashboard/src/services/remoteBackend.ts +++ b/app/ide-desktop/lib/dashboard/src/services/remoteBackend.ts @@ -155,20 +155,6 @@ export class RemoteBackend extends backendModule.Backend { throw new Error(message) } - /** Return the root directory id for the given user. */ - override rootDirectoryId( - user: backendModule.UserOrOrganization | null - ): backendModule.DirectoryId { - if (user != null && !user.id.startsWith('organization-')) { - this.logger.error(`User ID '${user.id}' does not start with 'organization-'`) - } - return backendModule.DirectoryId( - // `user` is only null when the user is offline, in which case the remote backend cannot - // be accessed anyway. - user?.id.replace(/^organization-/, `${backendModule.AssetType.directory}-`) ?? '' - ) - } - /** Return a list of all users in the same organization. */ override async listUsers(): Promise { const path = remoteBackendPaths.LIST_USERS_PATH diff --git a/app/ide-desktop/lib/dashboard/test-e2e/api.ts b/app/ide-desktop/lib/dashboard/test-e2e/api.ts index 3f919768abc..9faecdbea56 100644 --- a/app/ide-desktop/lib/dashboard/test-e2e/api.ts +++ b/app/ide-desktop/lib/dashboard/test-e2e/api.ts @@ -37,12 +37,13 @@ export async function mockApi(page: test.Page) { const defaultEmail = 'email@example.com' as backend.EmailAddress const defaultUsername = 'user name' const defaultOrganizationId = backend.UserOrOrganizationId('organization-placeholder id') - const defaultDirectoryId = backend.UserOrOrganizationId('directory-placeholder id') + const defaultDirectoryId = backend.DirectoryId('directory-placeholder id') const defaultUser: backend.UserOrOrganization = { email: defaultEmail, name: defaultUsername, id: defaultOrganizationId, isEnabled: true, + rootDirectoryId: defaultDirectoryId, } let currentUser: backend.UserOrOrganization | null = defaultUser const assetMap = new Map() @@ -297,11 +298,16 @@ export async function mockApi(page: test.Page) { // The type of the body sent by this app is statically known. // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const body: backend.CreateUserRequestBody = await request.postDataJSON() + const id = body.organizationId ?? defaultUser.id + const rootDirectoryId = backend.DirectoryId( + id.replace(/^organization-/, 'directory-') + ) currentUser = { email: body.userEmail, name: body.userName, - id: body.organizationId ?? defaultUser.id, + id, isEnabled: false, + rootDirectoryId, } await route.fulfill({ json: currentUser }) } else if (request.method() === 'GET') {