From b5bef7f2262df657bd27500573cc2d98ca632519 Mon Sep 17 00:00:00 2001 From: Alexey Date: Tue, 7 Mar 2023 23:14:30 +0800 Subject: [PATCH] CB-3214 suspense (#1527) * CB-3214 feat: enable strict mode for development * CB-3214 refactor: loaders --- .../packages/core-app/src/AppScreen/Main.tsx | 9 +- .../core-blocks/src/Loader/Loader.tsx | 5 +- .../src/Placeholder/Placeholder.tsx | 5 +- .../src/PropertiesTable/PropertyItem.tsx | 28 +++--- .../src/ResourcesHooks/useResource.ts | 48 +++++----- .../packages/core-blocks/src/getComputed.ts | 4 +- .../core-bootstrap/src/renderLayout.tsx | 4 +- webapp/packages/core-di/src/AppContext.tsx | 8 +- .../packages/core-ui/src/Tabs/TabsState.tsx | 29 ++++-- .../Form/ServerConfigurationFeaturesForm.tsx | 49 +++++----- .../ServerConfigurationPage.tsx | 40 +++++---- .../AuthConfigurationForm.tsx | 6 +- .../AuthConfiguration.tsx | 6 +- .../AuthenticationProviders.tsx | 89 +++++++++---------- .../Form/UserInfo/MetaParameterInfoForm.tsx | 46 ++++++++++ .../Form/UserInfo/MetaParameterInfoPanel.tsx | 67 ++------------ .../Form/UserInfo/MetaParameterInfoTab.tsx | 2 +- .../Form/UserInfo/MetaParametersForm.tsx | 76 ++++++++++++++++ .../GrantedConnections/GrantedConnections.tsx | 12 +-- .../useGrantedConnections.tsx | 13 ++- .../Users/Teams/GrantedUsers/GrantedUsers.tsx | 26 +++--- .../Teams/GrantedUsers/useGrantedUsers.tsx | 13 ++- .../Teams/Options/TeamMetaParameters.tsx | 44 +++++++++ .../Users/Teams/Options/TeamOptions.tsx | 24 +---- .../Administration/Users/Teams/TeamForm.tsx | 6 +- .../Users/Teams/TeamsTable/Team.tsx | 6 +- .../Administration/Users/UsersTable/User.tsx | 6 +- .../src/Dialog/AuthDialog.tsx | 26 +++--- .../src/Dialog/useAuthDialogState.ts | 6 +- .../ConnectionsTable/Connection.tsx | 12 +-- .../src/ConnectionForm/ConnectionForm.tsx | 4 +- .../DriverProperties/DriverProperties.tsx | 58 ++++++------ .../src/ConnectionForm/Options/Options.tsx | 18 +--- .../NavigationTreeSettings.tsx | 17 ++-- .../VirtualFolder/VirtualFolderPanel.tsx | 4 +- .../ObjectPropertyTable.tsx | 4 +- .../src/Tree/ResourceManagerTree.tsx | 10 ++- 37 files changed, 469 insertions(+), 361 deletions(-) create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoForm.tsx create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParametersForm.tsx create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/Options/TeamMetaParameters.tsx diff --git a/webapp/packages/core-app/src/AppScreen/Main.tsx b/webapp/packages/core-app/src/AppScreen/Main.tsx index 910fb81cfb..cf4211b5e8 100644 --- a/webapp/packages/core-app/src/AppScreen/Main.tsx +++ b/webapp/packages/core-app/src/AppScreen/Main.tsx @@ -9,7 +9,7 @@ import { observer } from 'mobx-react-lite'; import styled, { css } from 'reshadow'; -import { splitStyles, Split, ResizerControls, Pane, useSplitUserState, useStyles, Loader } from '@cloudbeaver/core-blocks'; +import { splitStyles, Split, ResizerControls, Pane, useSplitUserState, useStyles, Loader, getComputed } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { LeftBarPanelService, SideBarPanel, SideBarPanelService } from '@cloudbeaver/core-ui'; @@ -38,11 +38,8 @@ export const Main = observer(function Main() { const splitMainState = useSplitUserState('main'); const splitRightState = useSplitUserState('main-right'); - const activeSideBars = sideBarPanelService.tabsContainer.getDisplayed(); - const activeLeftBars = leftBarPanelService.tabsContainer.getDisplayed(); - - const sideBarDisabled = activeSideBars.length === 0; - const leftBarDisabled = activeLeftBars.length === 0; + const sideBarDisabled = getComputed(() => sideBarPanelService.tabsContainer.getDisplayed().length === 0); + const leftBarDisabled = getComputed(() => leftBarPanelService.tabsContainer.getDisplayed().length === 0); return styled(styles)( diff --git a/webapp/packages/core-blocks/src/Loader/Loader.tsx b/webapp/packages/core-blocks/src/Loader/Loader.tsx index 9ecbb4f027..f6d991bf5a 100644 --- a/webapp/packages/core-blocks/src/Loader/Loader.tsx +++ b/webapp/packages/core-blocks/src/Loader/Loader.tsx @@ -242,7 +242,10 @@ export const Loader = observer(function Loader({ return renderWrappedChildren(); } - if (exception && !loading && !hideException) { + if (exception && !loading) { + if (hideException) { + return null; + } return styled(style)( > = T & { @@ -35,8 +34,8 @@ export const Placeholder = observer(function Placeholder + <> {elements.map(({ id, component: Component }) => )} - + ); }); diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx index 169456d69a..78e6aa42be 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx @@ -183,6 +183,20 @@ export const PropertyItem = observer(function PropertyItem({ > {propertyValue} + {edited && !isDeletable && ( + + + + )} + {isDeletable && ( + + + + )} {(!readOnly && property.validValues && property.validValues.length > 0) && ( (function PropertyItem({ )} - {edited && !isDeletable && ( - - - - )} - {isDeletable && ( - - - - )} ); }); diff --git a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts index d0a208f591..9e4c481a4d 100644 --- a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts +++ b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { computed, observable, toJS, untracked, when } from 'mobx'; +import { action, computed, observable, reaction, toJS, untracked, when } from 'mobx'; import { useEffect, useContext, useState } from 'react'; import { IServiceConstructor, useService } from '@cloudbeaver/core-di'; @@ -271,6 +271,7 @@ export function useResource< } }, }), { + load: action, resourceException: computed, exception: observable.ref, loadingPromise: observable.ref, @@ -323,9 +324,9 @@ export function useResource< } // React Suspense block - if (refObj.loadingPromise) { - throw refObj.loadingPromise; - } + // if (refObj.loadingPromise) { + // throw refObj.loadingPromise; + // } if (this.loading) { throw this.resource.waitLoad(); @@ -395,30 +396,24 @@ export function useResource< }, { preloaded }); useEffect(() => { - let prevData: any; - const disposeDataUpdate = when( - () => { - try { - if (result.isError()) { - return false; - } - result.data; - return true; - } catch { - return false; - } + const disposeDataUpdate = reaction( + () => result.data, + (data, prev) => { + actions?.onData?.(data as any, resource, prev as any); }, - () => { - const newData = result.data; - refObj.actions?.onData?.(newData, resource, prevData); - prevData = newData; + { + onError: () => {}, + fireImmediately: true, } ); - const disposeErrorUpdate = when( - () => result.isError(), - () => { + const disposeErrorUpdate = reaction( + () => result.exception, + exception => { + if (!result.isError()) { + return; + } if (propertiesRef.errorContext) { - const errors = Array.isArray(result.exception) ? result.exception : [result.exception]; + const errors = Array.isArray(exception) ? exception : [exception]; for (const error of errors) { if (error) { @@ -426,7 +421,10 @@ export function useResource< } } } - refObj.actions?.onError?.(result.exception); + actions?.onError?.(exception); + }, + { + fireImmediately: true, } ); return () => { diff --git a/webapp/packages/core-blocks/src/getComputed.ts b/webapp/packages/core-blocks/src/getComputed.ts index b2c34eb7ba..eaba4bb973 100644 --- a/webapp/packages/core-blocks/src/getComputed.ts +++ b/webapp/packages/core-blocks/src/getComputed.ts @@ -8,6 +8,6 @@ import { computed } from 'mobx'; -export function getComputed(action: () => T): T { - return computed(action).get(); +export function getComputed(action: () => T, equals?: (a: T, b: T) => boolean): T { + return computed(action, { equals }).get(); } diff --git a/webapp/packages/core-bootstrap/src/renderLayout.tsx b/webapp/packages/core-bootstrap/src/renderLayout.tsx index 8e279d2f9d..9f52c70854 100644 --- a/webapp/packages/core-bootstrap/src/renderLayout.tsx +++ b/webapp/packages/core-bootstrap/src/renderLayout.tsx @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { StrictMode, Suspense } from 'react'; +import { Suspense } from 'react'; import { createRoot, Root } from 'react-dom/client'; import styled from 'reshadow'; @@ -52,7 +52,6 @@ export function renderLayout(serviceInjector: IServiceInjector): IRender { height: 100vh; } `( - // }> @@ -60,7 +59,6 @@ export function renderLayout(serviceInjector: IServiceInjector): IRender { - // )); }, renderError(exception?: any) { diff --git a/webapp/packages/core-di/src/AppContext.tsx b/webapp/packages/core-di/src/AppContext.tsx index cfaff4c7cd..7588dc2ee6 100644 --- a/webapp/packages/core-di/src/AppContext.tsx +++ b/webapp/packages/core-di/src/AppContext.tsx @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { createContext } from 'react'; +import { createContext, StrictMode } from 'react'; import type { IServiceInjector } from './IApp'; import { useAppLoadingScreen } from './useAppLoadingScreen'; @@ -19,5 +19,9 @@ interface Props { export const AppContext: React.FC> = function AppContext({ app, children }) { useAppLoadingScreen(); - return {children}; + return ( + // // problems with TabState when empty -> displayed state switch + {children} + // + ); }; diff --git a/webapp/packages/core-ui/src/Tabs/TabsState.tsx b/webapp/packages/core-ui/src/Tabs/TabsState.tsx index 8b4b3cd591..aaae6dd2ed 100644 --- a/webapp/packages/core-ui/src/Tabs/TabsState.tsx +++ b/webapp/packages/core-ui/src/Tabs/TabsState.tsx @@ -13,7 +13,7 @@ import { useTabState } from 'reakit/Tab'; import { useExecutor, useObjectRef, useObservableRef } from '@cloudbeaver/core-blocks'; import { Executor, ExecutorInterrupter } from '@cloudbeaver/core-executor'; -import { MetadataMap, MetadataValueGetter } from '@cloudbeaver/core-utils'; +import { isNull, isUndefined, MetadataMap, MetadataValueGetter } from '@cloudbeaver/core-utils'; import type { ITabData, ITabsContainer } from './TabsContainer/ITabsContainer'; import { TabsContext, ITabsContext } from './TabsContext'; @@ -96,12 +96,21 @@ export const TabsState = observer(function TabsState>({ tabList, }); - if (currentTabId !== undefined && currentTabId !== null) { + if ( + !isNull(currentTabId) + && !isUndefined(currentTabId) + ) { state.selectedId = currentTabId; dynamic.selectedId = currentTabId; } - if (displayed.length > 0 && dynamic.selectedId && selectedId) { + if ( + displayed.length > 0 + && !isNull(dynamic.selectedId) + && !isUndefined(dynamic.selectedId) + && !isNull(selectedId) + && !isUndefined(selectedId) + ) { const tabExists = displayed.includes(dynamic.selectedId); if (!tabExists) { @@ -134,7 +143,10 @@ export const TabsState = observer(function TabsState>({ }); useEffect(() => { - if (currentTabId !== undefined && currentTabId !== null) { + if ( + !isNull(currentTabId) + && !isUndefined(currentTabId) + ) { return; } @@ -145,13 +157,16 @@ export const TabsState = observer(function TabsState>({ }, [currentTabId, state.selectedId]); useEffect(() => { - if (state.selectedId) { + if ( + !isNull(state.selectedId) + && !isUndefined(state.selectedId) + ) { openExecutor.execute({ - tabId: state.selectedId!, + tabId: state.selectedId, props, }); } - }, []); + }, [!isNull(state.selectedId) && !isUndefined(state.selectedId)]); const value = useObservableRef>(() => ({ getTabInfo(tabId: string) { diff --git a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationFeaturesForm.tsx b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationFeaturesForm.tsx index 98a0757042..d66235a7b7 100644 --- a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationFeaturesForm.tsx +++ b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/Form/ServerConfigurationFeaturesForm.tsx @@ -11,7 +11,7 @@ import { useContext } from 'react'; import styled from 'reshadow'; import { AdministrationSettingsService } from '@cloudbeaver/core-administration'; -import { BASE_CONTAINERS_STYLES, FormContext, GroupTitle, Loader, PlaceholderComponent, Switch, useResource, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; +import { BASE_CONTAINERS_STYLES, FormContext, GroupTitle, PlaceholderComponent, Switch, useResource, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { FeaturesResource } from '@cloudbeaver/core-root'; import type { IConfigurationPlaceholderProps } from '@cloudbeaver/plugin-administration'; @@ -20,16 +20,21 @@ export const ServerConfigurationFeaturesForm: PlaceholderComponent {translate('administration_configuration_wizard_configuration_services_group')} - - {() => styled(styles)( - <> - {features.data.map(feature => ( - - {feature.label} - - ))} - - )} - + {features.data.map(feature => ( + + {feature.label} + + ))} ); }); diff --git a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationPage.tsx b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationPage.tsx index ab99415eee..51c7f9a3b4 100644 --- a/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationPage.tsx +++ b/webapp/packages/plugin-administration/src/ConfigurationWizard/ServerConfiguration/ServerConfigurationPage.tsx @@ -36,6 +36,10 @@ const styles = css` white-space: pre-wrap; line-height: 2; } + + Loader { + height: 100%; + } `; export const ServerConfigurationPage: AdministrationItemContentComponent = observer(function ServerConfigurationPage({ @@ -118,27 +122,29 @@ export const ServerConfigurationPage: AdministrationItemContentComponent = obser )} {() => styled(style)( - - - - {translate('administration_configuration_wizard_configuration_plugins')} - - - + + + + + {translate('administration_configuration_wizard_configuration_plugins')} + + + + + - - - - - + + + + )} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationForm.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationForm.tsx index 60af6b5b3c..983b5e0469 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationForm.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationForm.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import { useEffect } from 'react'; import styled, { css } from 'reshadow'; -import { Placeholder, useObjectRef, useExecutor, BASE_CONTAINERS_STYLES, IconOrImage, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; +import { Placeholder, useObjectRef, useExecutor, BASE_CONTAINERS_STYLES, IconOrImage, useTranslate, useStyles, Loader } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import type { AdminAuthProviderConfiguration } from '@cloudbeaver/core-sdk'; import { TabsState, TabList, UNDERLINE_TAB_STYLES, TabPanelList, BASE_TAB_STYLES } from '@cloudbeaver/core-ui'; @@ -149,7 +149,9 @@ export const AuthConfigurationForm = observer(function AuthConfigurationF - + + + diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationsTable/AuthConfiguration.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationsTable/AuthConfiguration.tsx index 6179972148..0b497fe002 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationsTable/AuthConfiguration.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/IdentityProviders/AuthConfigurationsTable/AuthConfiguration.tsx @@ -12,7 +12,7 @@ import styled, { css, use } from 'reshadow'; import { AuthProvidersResource } from '@cloudbeaver/core-authentication'; import { TableItem, TableColumnValue, TableItemSelect, TableItemExpand, - Placeholder, StaticImage, useResource, FieldCheckbox + Placeholder, StaticImage, useResource, FieldCheckbox, Loader } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import type { AdminAuthProviderConfiguration } from '@cloudbeaver/core-sdk'; @@ -65,7 +65,9 @@ export const AuthConfiguration = observer(function AuthConfiguration({ co - + + + ); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx index b088acccb5..489e0f61a5 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import React, { useContext } from 'react'; import styled from 'reshadow'; -import { AuthProviderService, AuthProvidersResource, AuthSettingsService, AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication'; +import { AuthProvider, AuthProviderService, AuthProvidersResource, AuthSettingsService, AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication'; import { BASE_CONTAINERS_STYLES, FormContext, Group, GroupTitle, Loader, PlaceholderComponent, Switch, useExecutor, useResource, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { CachedMapAllKey } from '@cloudbeaver/core-sdk'; @@ -33,8 +33,8 @@ export const AuthenticationProviders: PlaceholderComponent { - if (configurationWizard && provider.configurable) { + const providerList = providers.data.filter((provider): provider is AuthProvider => { + if (configurationWizard && provider?.configurable) { return false; } @@ -89,56 +89,51 @@ export const AuthenticationProviders: PlaceholderComponent ) : null} - - {() => styled(styles)( - <> - {providerList.map(provider => { - const links = authProviderService.getServiceDescriptionLinks(provider); - let disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat)); - const tooltip = disabled ? `Following services need to be enabled: "${provider.requiredFeatures.join(', ')}"` : ''; - if ( - !localProvider + {providerList.map(provider => { + const links = authProviderService.getServiceDescriptionLinks(provider); + let disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat)); + const tooltip = disabled ? `Following services need to be enabled: "${provider.requiredFeatures.join(', ')}"` : ''; + + if ( + !localProvider && primaryProvider?.id === provider.id && serverConfig.enabledAuthProviders?.length === 1 && serverConfig.enabledAuthProviders.includes(provider.id) - ) { - disabled = true; - } + ) { + disabled = true; + } - if (provider.private || (configurationWizard && (disabled || provider.id !== AUTH_PROVIDER_LOCAL_ID))) { - return null; - } + if (provider.private || (configurationWizard && (disabled || provider.id !== AUTH_PROVIDER_LOCAL_ID))) { + return null; + } - return ( - - {provider.description} - {links.map(link => { - const Description = link.description(); - return ; - })} - - )} - mod={['primary']} - disabled={disabled} - small - autoHide - > - {provider.label} - - ); - })} - - )} - + return ( + + {provider.description} + {links.map(link => { + const Description = link.description(); + return ; + })} + + )} + mod={['primary']} + disabled={disabled} + small + autoHide + > + {provider.label} + + ); + })} {configurationWizard && localProvider && ( diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoForm.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoForm.tsx new file mode 100644 index 0000000000..40e6e9463b --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoForm.tsx @@ -0,0 +1,46 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { observer } from 'mobx-react-lite'; +import styled from 'reshadow'; + +import { UserMetaParametersResource } from '@cloudbeaver/core-authentication'; +import { BASE_CONTAINERS_STYLES, Container, ObjectPropertyInfoForm, useResource, useStyles } from '@cloudbeaver/core-blocks'; +import type { UserInfo } from '@cloudbeaver/core-sdk'; +import type { ComponentStyle } from '@cloudbeaver/core-theming'; + +interface Props { + user: UserInfo; + className?: string; + style?: ComponentStyle; +} + +export const MetaParameterInfoForm = observer(function MetaParameterInfoForm({ + user, + className, + style, +}) { + const userMetaParameters = useResource(MetaParameterInfoForm, UserMetaParametersResource, undefined); + const styles = useStyles(style, BASE_CONTAINERS_STYLES); + + if (!userMetaParameters.data.length) { + return null; + } + + return styled(styles)( + + + + ); +}); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoPanel.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoPanel.tsx index 517c9196c0..d9666c5904 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoPanel.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoPanel.tsx @@ -9,14 +9,12 @@ import { observer } from 'mobx-react-lite'; import styled from 'reshadow'; -import { UserMetaParametersResource } from '@cloudbeaver/core-authentication'; -import { TabPanel } from '@cloudbeaver/core-ui'; -import { BASE_CONTAINERS_STYLES, ColoredContainer, Container, Group, InputField, Loader, ObjectPropertyInfoForm, useResource, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; - +import { useStyles } from '@cloudbeaver/core-blocks'; import type { UserInfo } from '@cloudbeaver/core-sdk'; import type { ComponentStyle } from '@cloudbeaver/core-theming'; +import { TabPanel } from '@cloudbeaver/core-ui'; -import { AuthProvidersList } from '../AuthProviders/ConfigurationsList'; +import { MetaParametersForm } from './MetaParametersForm'; interface Props { user: UserInfo; @@ -29,66 +27,11 @@ export const MetaParameterInfoPanel = observer(function MetaParameterInfo className, style, }) { - const userMetaParameters = useResource(MetaParameterInfoPanel, UserMetaParametersResource, undefined); - const styles = useStyles(style, BASE_CONTAINERS_STYLES); - const translate = useTranslate(); + const styles = useStyles(style); return styled(styles)( - - - - - - {translate('plugin_user_profile_info_id')} - - - {translate('plugin_user_profile_info_displayName')} - - - - - {() => userMetaParameters.data.length > 0 && styled(styles)( - - - - )} - - - - - - - - + ); }); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoTab.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoTab.tsx index fe2c9f61c8..b423bef863 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoTab.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParameterInfoTab.tsx @@ -9,9 +9,9 @@ import { observer } from 'mobx-react-lite'; import styled from 'reshadow'; +import type { ComponentStyle } from '@cloudbeaver/core-theming'; import { Tab } from '@cloudbeaver/core-ui'; -import type { ComponentStyle } from '@cloudbeaver/core-theming'; interface Props { className?: string; diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParametersForm.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParametersForm.tsx new file mode 100644 index 0000000000..d082a975dd --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/MetaParameters/Form/UserInfo/MetaParametersForm.tsx @@ -0,0 +1,76 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { observer } from 'mobx-react-lite'; +import styled from 'reshadow'; + +import { BASE_CONTAINERS_STYLES, ColoredContainer, Container, Group, InputField, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; +import type { UserInfo } from '@cloudbeaver/core-sdk'; +import type { ComponentStyle } from '@cloudbeaver/core-theming'; + +import { AuthProvidersList } from '../AuthProviders/ConfigurationsList'; +import { MetaParameterInfoForm } from './MetaParameterInfoForm'; + +interface Props { + user: UserInfo; + className?: string; + style?: ComponentStyle; +} + +export const MetaParametersForm = observer(function MetaParametersForm({ + user, + className, + style, +}) { + const styles = useStyles(style, BASE_CONTAINERS_STYLES); + const translate = useTranslate(); + + return styled(styles)( + + + + + + {translate('plugin_user_profile_info_id')} + + + {translate('plugin_user_profile_info_displayName')} + + + + + + + + + + + ); +}); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx index aafba41634..e88c7b079a 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx @@ -12,7 +12,7 @@ import styled, { css } from 'reshadow'; import { BASE_CONTAINERS_STYLES, ColoredContainer, Container, getComputed, Group, - InfoItem, Loader, TextPlaceholder, useResource, useStyles, useTranslate + InfoItem, Loader, TextPlaceholder, useAutoLoad, useResource, useStyles, useTranslate } from '@cloudbeaver/core-blocks'; import { Connection, ConnectionInfoProjectKey, ConnectionInfoResource, DBDriverResource, isCloudConnection } from '@cloudbeaver/core-connections'; import type { TLocalizationToken } from '@cloudbeaver/core-localization'; @@ -60,7 +60,7 @@ export const GrantedConnections: TabContainerPanelComponent = ob .map(project => project.id) ); - const dbDriverResource = useResource( + useResource( GrantedConnections, DBDriverResource, CachedMapAllKey, @@ -80,11 +80,7 @@ export const GrantedConnections: TabContainerPanelComponent = ob .filter(connection => state.state.grantedSubjects.includes(connection.id)) ); - useEffect(() => { - if (selected && !loaded) { - state.load(); - } - }); + useAutoLoad(state, selected && !loaded); if (!selected) { return null; @@ -103,7 +99,7 @@ export const GrantedConnections: TabContainerPanelComponent = ob } return styled(style)( - + {() => styled(style)( {!connections.length ? ( diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/useGrantedConnections.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/useGrantedConnections.tsx index ede71f60f9..e9d03b9a03 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/useGrantedConnections.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/useGrantedConnections.tsx @@ -13,12 +13,12 @@ import { useObservableRef } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { useTabState } from '@cloudbeaver/core-ui'; -import { isArraysEqual } from '@cloudbeaver/core-utils'; +import { ILoadableState, isArraysEqual } from '@cloudbeaver/core-utils'; import type { TeamFormMode } from '../ITeamFormProps'; import type { IGrantedConnectionsTabState } from './IGrantedConnectionsTabState'; -interface State { +interface State extends ILoadableState { state: IGrantedConnectionsTabState; changed: boolean; edit: () => void; @@ -36,6 +36,15 @@ export function useGrantedConnections(team: TeamInfo, mode: TeamFormMode): Reado get changed() { return !isArraysEqual(this.state.initialGrantedSubjects, this.state.grantedSubjects); }, + isLoading() { + return this.state.loading; + }, + isLoaded() { + return this.state.loaded; + }, + isError() { + return false; + }, edit() { this.state.editing = !this.state.editing; }, diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx index 69f9dca4d8..48b21c53ff 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx @@ -6,15 +6,13 @@ * you may not use this file except in compliance with the License. */ -import { computed } from 'mobx'; import { observer } from 'mobx-react-lite'; -import { useEffect, useMemo } from 'react'; import styled, { css } from 'reshadow'; -import { UsersResource } from '@cloudbeaver/core-authentication'; +import { AdminUser, UsersResource } from '@cloudbeaver/core-authentication'; import { - BASE_CONTAINERS_STYLES, ColoredContainer, Container, Group, - InfoItem, Loader, TextPlaceholder, useResource, useStyles, useTranslate + BASE_CONTAINERS_STYLES, ColoredContainer, Container, getComputed, Group, + InfoItem, Loader, TextPlaceholder, useAutoLoad, useResource, useStyles, useTranslate } from '@cloudbeaver/core-blocks'; import { CachedMapAllKey } from '@cloudbeaver/core-sdk'; import { TabContainerPanelComponent, useTab } from '@cloudbeaver/core-ui'; @@ -51,24 +49,20 @@ export const GrantedUsers: TabContainerPanelComponent = observer const state = useGrantedUsers(formState.config, formState.mode); const { selected } = useTab(tabId); - const users = useResource(GrantedUsers, UsersResource, selected ? CachedMapAllKey : null); + const users = useResource(GrantedUsers, UsersResource, CachedMapAllKey, { active: selected }); - const grantedUsers = useMemo(() => computed(() => users.resource.values - .filter(user => state.state.grantedUsers.includes(user.userId)) - ), [state.state.grantedUsers, users.resource]); + const grantedUsers = getComputed(() => users.data + .filter((user): user is AdminUser => !!user && state.state.grantedUsers.includes(user.userId)) + ); - useEffect(() => { - if (selected && !state.state.loaded) { - state.load(); - } - }, [selected, state.state.loaded]); + useAutoLoad(state, selected && !state.state.loaded); if (!selected) { return null; } return styled(style)( - + {() => styled(style)( {!users.resource.values.length ? ( @@ -80,7 +74,7 @@ export const GrantedUsers: TabContainerPanelComponent = observer {formState.mode === 'edit' && state.changed && } void; @@ -36,6 +36,15 @@ export function useGrantedUsers(team: TeamInfo, mode: TeamFormMode): Readonly(function TeamMetaParameters({ + state, +}) { + const teamMetaParameters = useResource(TeamMetaParameters, TeamMetaParametersResource, undefined); + const translate = useTranslate(); + const style = useStyles(BASE_CONTAINERS_STYLES); + + if (teamMetaParameters.data.length > 0) { + return null; + } + + return styled(style)( + + {translate('authentication_team_meta_parameters')} + + + ); +}); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/Options/TeamOptions.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/Options/TeamOptions.tsx index 12eb189fcb..2045db1bad 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/Options/TeamOptions.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/Options/TeamOptions.tsx @@ -10,13 +10,13 @@ import { observer } from 'mobx-react-lite'; import { useRef } from 'react'; import styled, { css } from 'reshadow'; -import { TeamMetaParametersResource } from '@cloudbeaver/core-authentication'; import { BASE_CONTAINERS_STYLES, ColoredContainer, Group, InputField, SubmittingForm, Textarea, useTranslate, useStyles, useResource, Loader, GroupTitle, ObjectPropertyInfoForm } from '@cloudbeaver/core-blocks'; import { ServerConfigResource } from '@cloudbeaver/core-root'; import type { TabContainerPanelComponent } from '@cloudbeaver/core-ui'; import type { ITeamFormProps } from '../ITeamFormProps'; import { Permissions } from './Permissions'; +import { TeamMetaParameters } from './TeamMetaParameters'; const styles = css` SubmittingForm { @@ -28,13 +28,10 @@ const styles = css` export const TeamOptions: TabContainerPanelComponent = observer(function TeamOptions({ state, }) { - const formRef = useRef(null); - - const translate = useTranslate(); const serverConfigResource = useResource(TeamOptions, ServerConfigResource, undefined); - const teamMetaParameters = useResource(TeamOptions, TeamMetaParametersResource, undefined); - const style = useStyles(BASE_CONTAINERS_STYLES, styles); + const formRef = useRef(null); + const translate = useTranslate(); const edit = state.mode === 'edit'; return styled(style)( @@ -74,20 +71,7 @@ export const TeamOptions: TabContainerPanelComponent = observer( {!serverConfigResource.resource.distributed && } - - {() => teamMetaParameters.data.length > 0 && styled(style)( - - {translate('authentication_team_meta_parameters')} - - - )} - + ); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamForm.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamForm.tsx index 7f38bab62e..0651dd1b53 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamForm.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamForm.tsx @@ -11,7 +11,7 @@ import { useEffect } from 'react'; import styled, { css } from 'reshadow'; import type { TeamInfo } from '@cloudbeaver/core-authentication'; -import { Placeholder, useObjectRef, useExecutor, BASE_CONTAINERS_STYLES, IconOrImage, useTranslate, useStyles } from '@cloudbeaver/core-blocks'; +import { Placeholder, useObjectRef, useExecutor, BASE_CONTAINERS_STYLES, IconOrImage, useTranslate, useStyles, Loader } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { TabsState, TabList, UNDERLINE_TAB_STYLES, TabPanelList, BASE_TAB_STYLES } from '@cloudbeaver/core-ui'; @@ -150,7 +150,9 @@ export const TeamForm = observer(function TeamForm({ - + + + diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/Team.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/Team.tsx index d4b3838c3b..54a6c82f5a 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/Team.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/Team.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import styled, { css, use } from 'reshadow'; import type { TeamInfo } from '@cloudbeaver/core-authentication'; -import { TableItem, TableColumnValue, TableItemSelect, TableItemExpand, Placeholder, useStyles } from '@cloudbeaver/core-blocks'; +import { TableItem, TableColumnValue, TableItemSelect, TableItemExpand, Placeholder, useStyles, Loader } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; @@ -53,7 +53,9 @@ export const Team = observer(function Team({ team }) { {team.teamName || ''} {team.description || ''} - + + + ); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/User.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/User.tsx index 166040da27..0ba5172c6a 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/User.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/User.tsx @@ -11,7 +11,7 @@ import styled, { css, use } from 'reshadow'; import { AdminUser, UsersResource } from '@cloudbeaver/core-authentication'; import { - TableItem, TableColumnValue, TableItemSelect, TableItemExpand, Placeholder, Checkbox, useTranslate + TableItem, TableColumnValue, TableItemSelect, TableItemExpand, Placeholder, Checkbox, useTranslate, Loader } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; @@ -76,7 +76,9 @@ export const User = observer(function User({ user, displayAuthRole, selec /> - + + + ); diff --git a/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx b/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx index e37127a2e5..5cf5eb96f7 100644 --- a/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx +++ b/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx @@ -184,21 +184,17 @@ export const AuthDialog: DialogComponent = observer(function )} - - {() => federate - ? ( - { - state.setActiveConfiguration(provider, configuration); - }} - onClose={rejectDialog} - /> - ) - : renderForm(state.activeProvider)} - + {federate ? ( + { + state.setActiveConfiguration(provider, configuration); + }} + onClose={rejectDialog} + /> + ) : renderForm(state.activeProvider)} {!federate && ( diff --git a/webapp/packages/plugin-authentication/src/Dialog/useAuthDialogState.ts b/webapp/packages/plugin-authentication/src/Dialog/useAuthDialogState.ts index cdada1b164..64ff6e88ce 100644 --- a/webapp/packages/plugin-authentication/src/Dialog/useAuthDialogState.ts +++ b/webapp/packages/plugin-authentication/src/Dialog/useAuthDialogState.ts @@ -26,7 +26,6 @@ interface IData { destroyed: boolean; configure: boolean; adminPageActive: boolean; - loadingState: ILoadableState; providers: AuthProvider[]; configurations: AuthProvider[]; @@ -52,7 +51,7 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string | const primaryId = authProvidersResource.resource.getPrimary(); const adminPageActive = administrationScreenService.isAdministrationPageActive; - const providers = authProvidersResource.resource.values + const providers = authProvidersResource.data .filter(notEmptyProvider) .sort(compareProviders); @@ -129,7 +128,6 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string | exception: null, authenticating: false, destroyed: false, - loadingState: authProvidersResource, get configure(): boolean { if (state.activeProvider) { @@ -197,7 +195,7 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string | } function notEmptyProvider(obj: any): obj is AuthProvider { - return typeof obj === 'object'; + return !!obj && typeof obj === 'object'; } function compareProviders(providerA: AuthProvider, providerB: AuthProvider): number { diff --git a/webapp/packages/plugin-connections-administration/src/Administration/Connections/ConnectionsTable/Connection.tsx b/webapp/packages/plugin-connections-administration/src/Administration/Connections/ConnectionsTable/Connection.tsx index 5639ae6ade..d4e282229b 100644 --- a/webapp/packages/plugin-connections-administration/src/Administration/Connections/ConnectionsTable/Connection.tsx +++ b/webapp/packages/plugin-connections-administration/src/Administration/Connections/ConnectionsTable/Connection.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import styled, { css } from 'reshadow'; import { - TableItem, TableColumnValue, TableItemSelect, TableItemExpand, StaticImage, Placeholder + TableItem, TableColumnValue, TableItemSelect, TableItemExpand, StaticImage, Placeholder, Loader } from '@cloudbeaver/core-blocks'; import { DatabaseConnection, DBDriverResource, IConnectionInfoParams } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; @@ -65,10 +65,12 @@ export const Connection = observer(function Connection({ connectionKey, c {projectName !== undefined && ( {projectName})} - + + + ); diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx index 7485cf8768..82d3800b9d 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx @@ -175,7 +175,9 @@ export const ConnectionForm = observer(function ConnectionForm({ - + + + diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx index 32fde09876..79f9dee4cc 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { computed, observable } from 'mobx'; +import { computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react-lite'; import { useMemo, useState } from 'react'; import styled, { css } from 'reshadow'; @@ -64,21 +64,23 @@ export const DriverProperties: TabContainerPanelComponent DriverProperties, DBDriverResource, { key: (selected && formState.config.driverId) || null, includes: ['includeDriverProperties'] as const }, - { - active: selected && !!formState.config.driverId, - onData: driver => { - for (const key of Object.keys(formState.config.properties)) { - if (driver.driverProperties.some(property => property.id === key) - || state.propertiesList.some(property => property.key === key)) { - continue; - } - - state.add(key, formState.config.properties[key]); - } - }, - } ); + runInAction(() => { + if (driver.data) { + for (const key of Object.keys(formState.config.properties)) { + if ( + driver.data.driverProperties.some(property => property.id === key) + || state.propertiesList.some(property => property.key === key) + ) { + continue; + } + + state.add(key, formState.config.properties[key]); + } + } + }); + const joinedProperties = useMemo(() => computed(() => ([ ...state.propertiesList, ...(driver.data?.driverProperties @@ -96,21 +98,17 @@ export const DriverProperties: TabContainerPanelComponent ])), [driver.data]); return styled(style)( - - {() => styled(style)( - - - - - - )} - + + + + + ); }); diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx index 83e3ad97d3..0011ca7062 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx @@ -99,17 +99,6 @@ export const Options: TabContainerPanelComponent = observe const optionsHook = useOptions(state); const { credentialsSavingEnabled } = useAdministrationSettings(); - const handleDriverSelect = useCallback(async (value?: string, name?: string, prev?: string) => { - if (!value) { - return; - } - - const prevDriver = prev ? await driverMap.resource.load(prev) : undefined; - const newDriver = await driverMap.resource.load(value); - - optionsHook.setDefaults(newDriver, prevDriver); - }, []); - const handleAuthModelSelect = useCallback(async (value?: string, name?: string, prev?: string) => { const model = applicableAuthModels.find(model => model?.id === value); @@ -125,10 +114,8 @@ export const Options: TabContainerPanelComponent = observe DBDriverResource, { key: config.driverId || null, includes: ['includeProviderProperties'] as const }, { - onData: (data, resource, prevData) => { - if (data.id !== prevData?.id) { - optionsHook.setDefaults(data); - } + onData: (data, resource, prevDriver) => { + optionsHook.setDefaults(data, prevDriver); }, } ); @@ -222,7 +209,6 @@ export const Options: TabContainerPanelComponent = observe loading={driverMap.isLoading()} tiny fill - onSelect={handleDriverSelect} > {translate('connections_connection_driver')} diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/NavigationTreeSettings.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/NavigationTreeSettings.tsx index 607fd30d74..bcad092362 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/NavigationTreeSettings.tsx +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/NavigationTreeSettings.tsx @@ -9,10 +9,9 @@ import { observer } from 'mobx-react-lite'; import styled, { css } from 'reshadow'; -import { BASE_CONTAINERS_STYLES, Group, Placeholder, PlaceholderElement, useStyles } from '@cloudbeaver/core-blocks'; +import { BASE_CONTAINERS_STYLES, Group, Loader, Placeholder, PlaceholderElement, useStyles } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; - import { ElementsTreeSettingsService, IElementsTreeSettingsProps } from './ElementsTreeSettingsService'; const expandStyles = css` @@ -47,12 +46,14 @@ export const NavigationTreeSettings = observer(function NavigationTreeSet return styled(styles)( - + + + ); diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/VirtualFolder/VirtualFolderPanel.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/VirtualFolder/VirtualFolderPanel.tsx index 3fda0aaf11..c9df37090e 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/VirtualFolder/VirtualFolderPanel.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/VirtualFolder/VirtualFolderPanel.tsx @@ -48,9 +48,7 @@ export const VirtualFolderPanel: NavNodeTransformViewComponent = observer(functi }); useEffect(() => { - untracked(() => { - navNodeViewService.logDuplicates(nodeId, duplicates); - }); + navNodeViewService.logDuplicates(nodeId, duplicates); }); const objects = dbObject.data diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx index 3d5e434ca8..10f0ddd07c 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx @@ -52,9 +52,7 @@ export const ObjectPropertyTable = observer(function O }); useEffect(() => { - untracked(() => { - navNodeViewService.logDuplicates(objectId, duplicates); - }); + navNodeViewService.logDuplicates(objectId, duplicates); }); const objects = dbObject.data.filter(object => nodes.includes(object?.id ?? '')) as DBObject[]; diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx b/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx index ad052f61ed..30b2e730c0 100644 --- a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx +++ b/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx @@ -10,12 +10,13 @@ import { observer } from 'mobx-react-lite'; import { useMemo } from 'react'; import styled, { css } from 'reshadow'; -import { useResource, useUserData } from '@cloudbeaver/core-blocks'; +import { getComputed, useResource, useUserData } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { NavNodeInfoResource, NavTreeResource, ProjectsNavNodeService } from '@cloudbeaver/core-navigation-tree'; import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects'; import { IResourceManagerParams, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager'; import { resourceKeyList } from '@cloudbeaver/core-sdk'; +import { isArraysEqual } from '@cloudbeaver/core-utils'; import { CaptureView } from '@cloudbeaver/core-view'; import { NavigationTreeService, ElementsTreeLoader, IElementsTreeSettings, createElementsTreeSettings, validateElementsTreeSettings, getNavigationTreeUserSettingsId } from '@cloudbeaver/plugin-navigation-tree'; @@ -79,9 +80,13 @@ export const ResourceManagerTree: React.FC = observer(function ResourceMa const resourceManagerService = useService(ResourceManagerService); const navTreeResource = useService(NavTreeResource); - const key: IResourceManagerParams[] = projectsService.activeProjectIds.map(id => ({ projectId: id })); + const key = getComputed( + () => projectsService.activeProjects.map(project => ({ projectId: project.id })), + isArraysEqual + ); useResource(ResourceManagerTree, ResourceManagerResource, resourceKeyList(key)); + const settings = useUserData( getNavigationTreeUserSettingsId(root), createElementsTreeSettings, @@ -166,7 +171,6 @@ export const ResourceManagerTree: React.FC = observer(function ResourceMa const settingsElements = useMemo(() => ([ProjectsSettingsPlaceholderElement]), []); - return styled(styles)(