Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CB-5205 fix: connection view switching #2984

Merged
merged 9 commits into from
Oct 15, 2024
25 changes: 25 additions & 0 deletions webapp/packages/core-connections/src/ContainerResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { AppAuthService } from '@cloudbeaver/core-authentication';
import { injectable } from '@cloudbeaver/core-di';
import { ExecutorInterrupter } from '@cloudbeaver/core-executor';
import { NavTreeResource } from '@cloudbeaver/core-navigation-tree';
import { CachedMapResource, isResourceAlias, type ResourceKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import { GraphQLService, type NavNodeInfoFragment } from '@cloudbeaver/core-sdk';
import { isNull } from '@cloudbeaver/core-utils';
Expand Down Expand Up @@ -40,12 +41,36 @@ interface ObjectContainerParams {
export class ContainerResource extends CachedMapResource<ObjectContainerParams, IStructContainers> {
constructor(
private readonly graphQLService: GraphQLService,
private readonly navTreeResource: NavTreeResource,
private readonly connectionInfoResource: ConnectionInfoResource,
appAuthService: AppAuthService,
) {
super();

appAuthService.requireAuthentication(this);
this.preloadResource(navTreeResource, key => {
if (isResourceAlias(key)) {
return '';
}
return ResourceKeyUtils.mapKey(
key,
key => this.connectionInfoResource.get(createConnectionParam(key.projectId, key.connectionId))?.nodePath || '',
);
});
this.navTreeResource.onDataOutdated.addHandler(key => {
ResourceKeyUtils.forEach(key, key => {
if (isResourceAlias(key)) {
return;
}
const connection = this.connectionInfoResource.getConnectionForNode(key);

if (!connection) {
return;
}

this.markOutdated(resourceKeyList(this.keys.filter(key => key.projectId === connection.projectId && key.connectionId === connection.id)));
});
});
this.preloadResource(connectionInfoResource, () => ConnectionInfoActiveProjectKey);
this.before(
ExecutorInterrupter.interrupter(key => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { createExtension, type IExtension, isExtension } from '@cloudbeaver/core-extensions';
import type { ILoadableState } from '@cloudbeaver/core-utils';

const objectLoaderProviderSymbol = Symbol('@extension/ObjectLoaderProvider');

export type IObjectLoaderProvider<T = never> = (context: T) => ILoadableState[];

export function objectLoaderProvider<T>(provider: IObjectLoaderProvider<T>) {
return createExtension<T>(provider, objectLoaderProviderSymbol);
}

export function isObjectLoaderProvider<T>(obj: IExtension<T>): obj is IObjectLoaderProvider<T> & IExtension<T> {
return isExtension(obj, objectLoaderProviderSymbol);
}
1 change: 1 addition & 0 deletions webapp/packages/core-connections/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export * from './extensions/IObjectCatalogProvider.js';
export * from './extensions/IObjectCatalogSetter.js';
export * from './extensions/IObjectSchemaProvider.js';
export * from './extensions/IObjectSchemaSetter.js';
export * from './extensions/IObjectLoaderProvider.js';
export * from './extensions/IExecutionContextProvider.js';
export * from './NavTree/ConnectionNavNodeService.js';
export * from './NavTree/NavNodeExtensionsService.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,16 @@ export class NavTreeResource extends CachedMapResource<string, string[], Record<
}

async refreshTree(navNodeId: string, silent = false): Promise<void> {
await this.graphQLService.sdk.navRefreshNode({
nodePath: navNodeId,
});
this.performUpdate(navNodeId, [], async () => {
await this.graphQLService.sdk.navRefreshNode({
nodePath: navNodeId,
});

if (!silent) {
this.markTreeOutdated(navNodeId);
}
await this.onNodeRefresh.execute(navNodeId);
if (!silent) {
this.markOutdated(navNodeId);
}
await this.onNodeRefresh.execute(navNodeId);
});
}

markTreeOutdated(navNodeId: ResourceKeySimple<string>): void {
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(

const menu = useRef<IMenuState>();

useAutoLoad({ name: `${ContextMenu.name}(${menuData.menu.id})` }, menuData.loaders, !lazy, menuVisible);
useAutoLoad({ name: `${ContextMenu.name}(${menuData.menu.id})` }, menuData.loaders, !lazy, menuVisible, true);

const handlers = useObjectRef(
() => ({
Expand Down
4 changes: 2 additions & 2 deletions webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const MenuBar = observer<IMenuBarProps, HTMLDivElement>(
const mergedRef = useMergeRefs(ref, refNav);
const styles = useS(style);
const items = menu.items;
useAutoLoad(MenuBar, menu.loaders);
useAutoLoad(MenuBar, menu.loaders, true, false, true);

if (!items.length) {
return null;
Expand All @@ -67,7 +67,7 @@ export const MenuBar = observer<IMenuBarProps, HTMLDivElement>(
return (
<SContext registry={styleRegistry}>
<div ref={mergedRef} className={s(styles, { menuBar: true }, className)} tabIndex={0} {...props}>
<Loader suspense small>
<Loader suspense small inline>
{items.map(item => (
<MenuBarElement key={item.id} item={item} menuData={menu} nestedMenuSettings={nestedMenuSettings} rtl={rtl} />
))}
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/core-ui/src/ContextMenu/SubMenuElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const SubMenuElement = observer<ISubMenuElementProps, HTMLButtonElement>(

const handler = subMenuData.handler;
const hidden = getComputed(() => handler?.isHidden?.(subMenuData.context));
useAutoLoad(SubMenuElement, subMenuData.loaders, !hidden, visible);
useAutoLoad(SubMenuElement, subMenuData.loaders, !hidden, visible, true);

const handlers = useObjectRef(
() => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export class ConnectionSchemaManagerBootstrap extends Bootstrap {

return [
...this.appAuthService.loaders,
...this.connectionSchemaManagerService.currentObjectLoaders,
getCachedMapResourceLoaderState(this.containerResource, () => ({
...activeConnectionKey,
catalogId: this.connectionSchemaManagerService.activeObjectCatalogId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import {
type IExecutionContextProvider,
type IObjectCatalogProvider,
type IObjectCatalogSetter,
type IObjectLoaderProvider,
type IObjectSchemaProvider,
type IObjectSchemaSetter,
isConnectionProvider,
isConnectionSetter,
isExecutionContextProvider,
isObjectCatalogProvider,
isObjectCatalogSetter,
isObjectLoaderProvider,
isObjectSchemaProvider,
isObjectSchemaSetter,
type IStructContainers,
Expand All @@ -44,6 +46,7 @@ import {
isProjectSetterState,
} from '@cloudbeaver/core-projects';
import { CachedMapAllKey } from '@cloudbeaver/core-resource';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import { type ITab, NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';

export interface IConnectionInfo {
Expand All @@ -61,6 +64,7 @@ interface IActiveItem<T> {
getCurrentSchemaId?: IObjectSchemaProvider<T>;
getCurrentCatalogId?: IObjectCatalogProvider<T>;
getCurrentExecutionContext?: IExecutionContextProvider<T>;
getCurrentLoader?: IObjectLoaderProvider<T>;
changeConnectionId?: IConnectionSetter<T>;
changeProjectId?: IProjectSetter<T>;
changeCatalogId?: IObjectCatalogSetter<T>;
Expand Down Expand Up @@ -132,6 +136,14 @@ export class ConnectionSchemaManagerService {
return this.activeItem.getCurrentExecutionContext(this.activeItem.context);
}

get currentObjectLoaders(): ILoadableState[] {
if (!this.activeItem?.getCurrentLoader) {
return [];
}

return this.activeItem.getCurrentLoader(this.activeItem.context);
}

get currentObjectSchemaId(): string | undefined {
if (this.pendingSchemaId !== null) {
return this.pendingSchemaId;
Expand Down Expand Up @@ -271,6 +283,7 @@ export class ConnectionSchemaManagerService {
currentObjectCatalogId: computed,
activeObjectCatalogId: computed,
currentObjectSchemaId: computed,
currentObjectLoaders: computed,
isConnectionChangeable: computed,
isObjectCatalogChangeable: computed,
isObjectSchemaChangeable: computed,
Expand Down Expand Up @@ -456,6 +469,9 @@ export class ConnectionSchemaManagerService {
.on(isExecutionContextProvider, extension => {
item.getCurrentExecutionContext = extension;
})
.on(isObjectLoaderProvider, extension => {
item.getCurrentLoader = extension;
})

.on(isProjectSetter, extension => {
item.changeProjectId = extension;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class ElementsTreeToolsMenuService {
state.filter = !state.filter;
}

private elementsTreeActionHandler(contexts: IDataContextProvider, action: IAction) {
private async elementsTreeActionHandler(contexts: IDataContextProvider, action: IAction) {
const tree = contexts.get(DATA_CONTEXT_ELEMENTS_TREE);

if (tree === undefined) {
Expand All @@ -178,6 +178,9 @@ export class ElementsTreeToolsMenuService {
tree.collapse();
break;
case ACTION_LINK_OBJECT: {
for (const loader of this.connectionSchemaManagerService.currentObjectLoaders) {
await loader.load();
}
const navNode = this.connectionSchemaManagerService.activeNavNode;

if (navNode?.path.includes(tree.baseRoot)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
type IConnectionInfoParams,
objectCatalogProvider,
objectCatalogSetter,
objectLoaderProvider,
objectSchemaProvider,
objectSchemaSetter,
} from '@cloudbeaver/core-connections';
Expand All @@ -33,7 +34,7 @@ import { NotificationService } from '@cloudbeaver/core-events';
import { Executor, ExecutorInterrupter, type IExecutionContextProvider } from '@cloudbeaver/core-executor';
import { NavNodeInfoResource, NodeManagerUtils, objectNavNodeProvider } from '@cloudbeaver/core-navigation-tree';
import { projectProvider, projectSetter, projectSetterState } from '@cloudbeaver/core-projects';
import { resourceKeyList, type ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import { getCachedMapResourceLoaderState, resourceKeyList, type ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import type { NavNodeInfoFragment } from '@cloudbeaver/core-sdk';
import { isArraysEqual } from '@cloudbeaver/core-utils';
import { type ITab, type ITabOptions, NavigationTabsService, TabHandler } from '@cloudbeaver/plugin-navigation-tabs';
Expand Down Expand Up @@ -95,6 +96,7 @@ export class SqlEditorTabService extends Bootstrap {
projectProvider(this.getProjectId.bind(this)),
connectionProvider(this.getConnectionId.bind(this)),
objectCatalogProvider(this.getObjectCatalogId.bind(this)),
objectLoaderProvider(this.getObjectLoader.bind(this)),
objectSchemaProvider(this.getObjectSchemaId.bind(this)),
executionContextProvider(this.getExecutionContext.bind(this)),
projectSetter(this.setProjectId.bind(this)),
Expand Down Expand Up @@ -209,8 +211,6 @@ export class SqlEditorTabService extends Bootstrap {

const parents = this.navNodeInfoResource.getParents(nodeId);

untracked(() => this.navNodeInfoResource.load(nodeId!));

return {
nodeId,
path: parents,
Expand Down Expand Up @@ -327,6 +327,34 @@ export class SqlEditorTabService extends Bootstrap {
return createConnectionParam(context.projectId, context.connectionId);
}

private getObjectLoader(tab: ITab<ISqlEditorTabState>) {
const executionContextComputed = computed(() => this.sqlDataSourceService.get(tab.handlerState.editorId)?.executionContext);

const connectionKeyComputed = computed(() => {
const executionContext = executionContextComputed.get();

if (!executionContext) {
return null;
}

return createConnectionParam(executionContext.projectId, executionContext.connectionId);
});

return [
getCachedMapResourceLoaderState(this.connectionInfoResource, () => connectionKeyComputed.get()),
getCachedMapResourceLoaderState(this.connectionExecutionContextResource, () => executionContextComputed.get()?.id || null),
getCachedMapResourceLoaderState(this.containerResource, () => connectionKeyComputed.get()),
// TODO: maybe we need it for this.getNavNode to work properly, but it's seems working without it
// getCachedMapResourceLoaderState(this.navNodeInfoResource, () => {
// if (this.containerResource.isLoadable(connectionKey)) {
// return null;
// }
// console.log('node:', this.getNavNode(tab)?.nodeId || null);
// return this.getNavNode(tab)?.nodeId || null;
// }),
];
}

private getObjectCatalogId(tab: ITab<ISqlEditorTabState>) {
const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId);
const context = this.connectionExecutionContextResource.get(dataSource?.executionContext?.id ?? '');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@
display: none;
}
}

.appStateMenu > .loader {
height: 100%;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { observer } from 'mobx-react-lite';

import { AppAuthService } from '@cloudbeaver/core-authentication';
import { s, SContext, type StyleRegistry, useS } from '@cloudbeaver/core-blocks';
import { Loader, s, SContext, type StyleRegistry, useS } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { MenuBar, MenuBarItemStyles, MenuBarStyles } from '@cloudbeaver/core-ui';
import { useMenu } from '@cloudbeaver/core-view';
Expand Down Expand Up @@ -46,7 +46,9 @@ export const AppStateMenu = observer(function AppStateMenu() {
return (
<SContext registry={registry}>
<div className={s(styles, { menuWrapper: true, appStateMenu: true })}>
<MenuBar menu={menu} nestedMenuSettings={{ modal: true }} />
<Loader className={s(styles, { loader: true }, 'secondary')} secondary suspense small inline>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we duplicate secondary as class and secondary as prop here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cuz it can be another loader in MenuBar to display is also as secondary

<MenuBar menu={menu} nestedMenuSettings={{ modal: true }} />
</Loader>
</div>
</SContext>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { observer } from 'mobx-react-lite';

import { AppAuthService } from '@cloudbeaver/core-authentication';
import { s, useS } from '@cloudbeaver/core-blocks';
import { Loader, s, useS } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { MenuBar } from '@cloudbeaver/core-ui';
import { useMenu } from '@cloudbeaver/core-view';
Expand All @@ -27,7 +27,9 @@ export const MainMenu = observer(function MainMenu() {

return (
<div className={s(styles, { menuWrapper: true })}>
<MenuBar menu={menu} nestedMenuSettings={{ modal: true }} />
<Loader className={s(styles, { loader: true }, 'secondary')} secondary suspense small inline>
<MenuBar menu={menu} nestedMenuSettings={{ modal: true }} />
</Loader>
</div>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
.menuWrapper {
display: flex;
height: 100%;

&.loader {
height: 100%;
}
}
Loading