From 6830e34b2a2efc12c6ea983948bcc53ce63d42cf Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Thu, 12 Dec 2024 11:27:12 +0100 Subject: [PATCH 1/4] CB-5933 Connection state is not synchronized with other resources triggering connection state (#3119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CB-5933. Added event for disconnect datasource * CB-5933. Added to gql * CB-5933 adds handlers for disconnected/connected events * CB-5933. Rename topic * CB-5933. Rename topic * CB-5933. Added event for connect to database * СB-5933 adds ConnectionDisconnectEventHandler * CB-5933. Fixed typo * CB-5933 fixes disconnect handler + adds connect handler for connections * CB-5933 fix for connected event * СB-5933 marks outdated connected/disconnected connections on event handling * CB-5933 cleanup * CB-5933 pr fixes * CB-5933 pr fixes * CB-5933. Refactor after review --------- Co-authored-by: denis.sinelnikov Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> --- .../schema/service.events.graphqls | 21 ++++++++++++ .../server/jobs/WebDataSourceMonitorJob.java | 17 +++++----- .../service/core/impl/WebServiceCore.java | 13 ++++++- .../src/ConnectionInfoResource.ts | 34 +++++++++++++++++++ .../src/ConnectionStateEventHandler.ts | 26 ++++++++++++++ .../packages/core-connections/src/manifest.ts | 1 + 6 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 webapp/packages/core-connections/src/ConnectionStateEventHandler.ts diff --git a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls index 3c506edd11..a970eae7f4 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls @@ -20,6 +20,9 @@ enum CBServerEventId { cb_datasource_folder_updated, cb_datasource_folder_deleted, + cb_datasource_disconnected, + cb_datasource_connected, + cb_rm_resource_created, cb_rm_resource_updated, cb_rm_resource_deleted, @@ -53,6 +56,7 @@ enum CBEventTopic { cb_object_permissions, cb_subject_permissions, cb_database_output_log, + cb_datasource_connection, cb_delete_temp_folder } @@ -178,6 +182,23 @@ type WSOutputLogInfo { # Add more fields as needed } +# Datasource disconnect event +type WSDataSourceDisconnectEvent implements CBServerEvent { + id: CBServerEventId! + topicId: CBEventTopic + connectionId: String! + projectId: String! + timestamp: Int! +} +# Datasource connect event +type WSDataSourceConnectEvent implements CBServerEvent { + id: CBServerEventId! + topicId: CBEventTopic + connectionId: String! + projectId: String! + timestamp: Int! +} + extend type Query { emptyEvent: Boolean } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java index 183acc805f..3eb1ab6e1a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java @@ -23,6 +23,7 @@ import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.app.DBPPlatform; import org.jkiss.dbeaver.model.app.DBPProject; +import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; import org.jkiss.dbeaver.runtime.jobs.DataSourceMonitorJob; @@ -57,14 +58,14 @@ protected void doJob() { protected void showNotification(@NotNull DBPDataSource dataSource) { final DBPProject project = dataSource.getContainer().getProject(); if (project.getWorkspaceSession() instanceof WebSession webSession) { - // TODO: Add new event for disconnect datasource - webSession.addSessionEvent(WSDataSourceEvent.update( - webSession.getSessionId(), - webSession.getUserId(), - project.getId(), - List.of(dataSource.getContainer().getId()), - WSDataSourceProperty.CONFIGURATION - )); + webSession.addSessionEvent( + new WSDataSourceDisconnectEvent( + project.getId(), + dataSource.getContainer().getId(), + webSession.getSessionId(), + webSession.getUserId() + ) + ); } } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index 60f4edbb33..e9e78767bb 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -55,6 +55,7 @@ import org.jkiss.dbeaver.model.secret.DBSSecretController; import org.jkiss.dbeaver.model.secret.DBSSecretValue; import org.jkiss.dbeaver.model.websocket.WSConstants; +import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceConnectEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; @@ -366,7 +367,17 @@ public WebConnectionInfo initConnection( boolean oldSavePassword = dataSourceContainer.isSavePassword(); try { - dataSourceContainer.connect(webSession.getProgressMonitor(), true, false); + boolean connect = dataSourceContainer.connect(webSession.getProgressMonitor(), true, false); + if (connect) { + webSession.addSessionEvent( + new WSDataSourceConnectEvent( + projectId, + connectionId, + webSession.getSessionId(), + webSession.getUserId() + ) + ); + } } catch (Exception e) { throw new DBWebException("Error connecting to database", e); } finally { diff --git a/webapp/packages/core-connections/src/ConnectionInfoResource.ts b/webapp/packages/core-connections/src/ConnectionInfoResource.ts index fb975d1612..5692c2fc0a 100644 --- a/webapp/packages/core-connections/src/ConnectionInfoResource.ts +++ b/webapp/packages/core-connections/src/ConnectionInfoResource.ts @@ -40,6 +40,7 @@ import { schemaValidationError } from '@cloudbeaver/core-utils'; import { CONNECTION_INFO_PARAM_SCHEMA, type IConnectionInfoParams } from './CONNECTION_INFO_PARAM_SCHEMA.js'; import { ConnectionInfoEventHandler, type IConnectionInfoEvent } from './ConnectionInfoEventHandler.js'; +import { ConnectionStateEventHandler, type IWsDataSourceConnectEvent, type IWsDataSourceDisconnectEvent } from './ConnectionStateEventHandler.js'; import type { DatabaseConnection } from './DatabaseConnection.js'; import { DBDriverResource } from './DBDriverResource.js'; import { parseConnectionKey } from './parseConnectionKey.js'; @@ -97,6 +98,7 @@ export class ConnectionInfoResource extends CachedMapResource( + ServerEventId.CbDatasourceDisconnected, + async data => { + const key: IConnectionInfoParams = { + projectId: data.projectId, + connectionId: data.connectionId, + }; + + if (this.isConnected(key) && !this.isConnecting(key)) { + this.markOutdated(key); + } + }, + undefined, + this, + ); + + connectionStateEventHandler.onEvent( + ServerEventId.CbDatasourceConnected, + async data => { + const key: IConnectionInfoParams = { + projectId: data.projectId, + connectionId: data.connectionId, + }; + + if (!this.isConnected(key) && !this.isConnecting(key)) { + this.markOutdated(key); + } + }, + undefined, + this, + ); + connectionInfoEventHandler.onEvent>( ServerEventId.CbDatasourceUpdated, key => { diff --git a/webapp/packages/core-connections/src/ConnectionStateEventHandler.ts b/webapp/packages/core-connections/src/ConnectionStateEventHandler.ts new file mode 100644 index 0000000000..41da8d3937 --- /dev/null +++ b/webapp/packages/core-connections/src/ConnectionStateEventHandler.ts @@ -0,0 +1,26 @@ +/* + * 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 { injectable } from '@cloudbeaver/core-di'; +import { type ISessionEvent, type SessionEventId, SessionEventSource, SessionEventTopic, TopicEventHandler } from '@cloudbeaver/core-root'; +import type { WsDataSourceConnectEvent, WsDataSourceDisconnectEvent } from '@cloudbeaver/core-sdk'; + +export type IWsDataSourceDisconnectEvent = WsDataSourceDisconnectEvent; +export type IWsDataSourceConnectEvent = WsDataSourceConnectEvent; + +type ConnectionStateEvent = IWsDataSourceConnectEvent | IWsDataSourceDisconnectEvent; + +@injectable() +export class ConnectionStateEventHandler extends TopicEventHandler { + constructor(sessionEventSource: SessionEventSource) { + super(SessionEventTopic.CbDatasourceConnection, sessionEventSource); + } + + map(event: any): ConnectionStateEvent { + return event; + } +} diff --git a/webapp/packages/core-connections/src/manifest.ts b/webapp/packages/core-connections/src/manifest.ts index d381194433..3bb1690a91 100644 --- a/webapp/packages/core-connections/src/manifest.ts +++ b/webapp/packages/core-connections/src/manifest.ts @@ -33,5 +33,6 @@ export const manifest: PluginManifest = { () => import('./ConnectionFolderEventHandler.js').then(m => m.ConnectionFolderEventHandler), () => import('./ConnectionsSettingsService.js').then(m => m.ConnectionsSettingsService), () => import('./ConnectionPublicSecretsResource.js').then(m => m.ConnectionPublicSecretsResource), + () => import('./ConnectionStateEventHandler.js').then(m => m.ConnectionStateEventHandler), ], }; From 32e50c40f4e1f29df081abfd62c4411487292cf0 Mon Sep 17 00:00:00 2001 From: DenisSinelnikov <142215442+DenisSinelnikov@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:33:18 +0400 Subject: [PATCH 2/4] CB-5945. Mark deprecate for template (#3123) * CB-5933. Fixed typo * CB-5945. Refactor after review * CB-5945. Refactor after review --------- Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com> Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com> --- .../io.cloudbeaver.server/schema/service.core.graphqls | 10 +++++----- .../io/cloudbeaver/service/core/DBWServiceCore.java | 1 + .../cloudbeaver/service/core/impl/WebServiceCore.java | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index 8980b26d0b..ccd536c6a4 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -346,7 +346,7 @@ type ConnectionInfo { properties: Object - template: Boolean! + template: Boolean! @deprecated connected: Boolean! provided: Boolean! readOnly: Boolean! @@ -492,7 +492,7 @@ input ConnectionConfig { description: String # ID of template connection - templateId: ID + templateId: ID @deprecated # ID of database driver driverId: ID @@ -519,7 +519,7 @@ input ConnectionConfig { autocommit: Boolean # Return template connection state - template: Boolean + template: Boolean @deprecated # Return read-only connection state readOnly: Boolean @@ -579,7 +579,7 @@ extend type Query { userConnections( projectId: ID, id: ID, projectIds: [ID!] ): [ ConnectionInfo! ]! # Return list of template connections by project ID - templateConnections( projectId: ID ): [ ConnectionInfo! ]! + templateConnections( projectId: ID ): [ ConnectionInfo! ]! @deprecated # List of connection folders connectionFolders( projectId: ID, path: ID ): [ ConnectionFolderInfo! ]! @@ -621,7 +621,7 @@ extend type Mutation { deleteConnection( id: ID!, projectId: ID ): Boolean! # Create new custom connection from template - createConnectionFromTemplate( templateId: ID!, projectId: ID!, connectionName: String ): ConnectionInfo! + createConnectionFromTemplate( templateId: ID!, projectId: ID!, connectionName: String ): ConnectionInfo! @deprecated # Create new folder for connections createConnectionFolder(parentFolderPath: ID, folderName: String!, projectId: ID ): ConnectionFolderInfo! diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java index 48f78fbe7a..c03064c1ac 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java @@ -146,6 +146,7 @@ boolean deleteConnection( @NotNull String connectionId) throws DBWebException; @WebAction + @Deprecated WebConnectionInfo createConnectionFromTemplate( @NotNull WebSession webSession, @NotNull String projectId, diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index e9e78767bb..408095d615 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -611,6 +611,7 @@ public boolean deleteConnection( } @Override + @Deprecated public WebConnectionInfo createConnectionFromTemplate( @NotNull WebSession webSession, @NotNull String projectId, From 2f80d1ce13826b007d49d6c467e59c594771c9bb Mon Sep 17 00:00:00 2001 From: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:19:34 +0100 Subject: [PATCH 3/4] CB-3332 shortcuts improvements (#3126) * CB-3332 refactor: data-viewer shortcuts move them to the plugin folder, make them IKeyBinding shape to use in transformKeys * CB-3332 refactor: change ctrl hotkey to mod * CB-3332 refactor: use new DATA_VIEWER_SHORTCUTS, don't replace backspace * CB-3332 refactor: remove redundant shortcuts, update naming * CB-3332 feat: introduce new shortcuts for rows managing In order to let mac users use shortcuts, Insert key was changed to KeyR * CB-3332 feat: move shift key to the beginning of all shortcuts --------- Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> --- .../KeyBinding/Bindings/KEY_BINDING_REDO.ts | 2 +- .../src/DataGrid/DATA_GRID_BINDINGS.ts | 29 +++++++++++++++++++ .../src/DataGrid/DataGridTable.tsx | 4 +-- .../plugin-data-spreadsheet-new/src/index.ts | 1 + webapp/packages/plugin-help/package.json | 1 + .../src/Shortcuts/SHORTCUTS_DATA.ts | 28 +++++++----------- webapp/packages/plugin-help/tsconfig.json | 3 ++ .../KEY_BINDING_ENABLE_FILTER.ts | 2 +- .../ElementsTree/KEY_BINDING_COLLAPSE_ALL.ts | 2 +- .../ElementsTree/KEY_BINDING_LINK_OBJECT.ts | 2 +- .../KEY_BINDING_SQL_EDITOR_EXECUTE.ts | 2 +- .../KEY_BINDING_SQL_EDITOR_EXECUTE_NEW.ts | 2 +- .../bindings/KEY_BINDING_SQL_EDITOR_FORMAT.ts | 2 +- ..._BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN.ts | 2 +- 14 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DATA_GRID_BINDINGS.ts diff --git a/webapp/packages/core-view/src/Action/KeyBinding/Bindings/KEY_BINDING_REDO.ts b/webapp/packages/core-view/src/Action/KeyBinding/Bindings/KEY_BINDING_REDO.ts index 55505ac576..367de24bc9 100644 --- a/webapp/packages/core-view/src/Action/KeyBinding/Bindings/KEY_BINDING_REDO.ts +++ b/webapp/packages/core-view/src/Action/KeyBinding/Bindings/KEY_BINDING_REDO.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '../createKeyBinding.js'; export const KEY_BINDING_REDO = createKeyBinding({ id: 'redo', - keys: ['mod+y', 'mod+shift+z'], + keys: ['mod+y', 'shift+mod+z'], preventDefault: true, }); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DATA_GRID_BINDINGS.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DATA_GRID_BINDINGS.ts new file mode 100644 index 0000000000..a30842c03c --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DATA_GRID_BINDINGS.ts @@ -0,0 +1,29 @@ +/* + * 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 type { IKeyBinding } from '@cloudbeaver/core-view'; + +/* these consts are only used for the user interface in Shortcuts popup, actual bindings in DataGridTable.tsx */ +export const KEY_BINDING_REVERT_INLINE_EDITOR_CHANGES: IKeyBinding = { + id: 'data-viewer-revert-inline-editor-changes', + keys: ['Escape'], +}; + +export const KEY_BINDING_ADD_NEW_ROW: IKeyBinding = { + id: 'data-viewer-add-new-row', + keys: ['Alt+R'], +}; + +export const KEY_BINDING_DUPLICATE_ROW: IKeyBinding = { + id: 'data-viewer-duplicate-row', + keys: ['Shift+Alt+R'], +}; + +export const KEY_BINDING_DELETE_ROW: IKeyBinding = { + id: 'data-viewer-delete-row', + keys: ['Delete'], +}; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx index 003c8672aa..da7b068bdb 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -248,9 +248,9 @@ export const DataGridTable = observer(function DataGridT tableData.editor.revert(...activeElements); return; } - case 'Insert': { + case 'KeyR': { if (event.altKey) { - if (event.ctrlKey || event.metaKey) { + if (event.shiftKey) { tableData.editor.duplicate(...activeRows); } else { tableData.editor.add(cell); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/index.ts b/webapp/packages/plugin-data-spreadsheet-new/src/index.ts index 73ac170d73..25a0daff3b 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/index.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/index.ts @@ -6,3 +6,4 @@ * you may not use this file except in compliance with the License. */ export * from './manifest.js'; +export * from './DataGrid/DATA_GRID_BINDINGS.js'; diff --git a/webapp/packages/plugin-help/package.json b/webapp/packages/plugin-help/package.json index 1c2e4f93c2..4fda9cd56a 100644 --- a/webapp/packages/plugin-help/package.json +++ b/webapp/packages/plugin-help/package.json @@ -29,6 +29,7 @@ "@cloudbeaver/core-routing": "^0", "@cloudbeaver/core-utils": "^0", "@cloudbeaver/core-view": "^0", + "@cloudbeaver/plugin-data-spreadsheet-new": "^0", "@cloudbeaver/plugin-navigation-tree": "^0", "@cloudbeaver/plugin-sql-editor": "^0", "@cloudbeaver/plugin-top-app-bar": "^0", diff --git a/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts b/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts index bf4158577f..c91b611c88 100644 --- a/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts +++ b/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts @@ -7,6 +7,12 @@ */ import { getOS, OperatingSystem } from '@cloudbeaver/core-utils'; import { getCommonAndOSSpecificKeys, type IKeyBinding, KEY_BINDING_OPEN_IN_TAB, KEY_BINDING_REDO, KEY_BINDING_UNDO } from '@cloudbeaver/core-view'; +import { + KEY_BINDING_ADD_NEW_ROW, + KEY_BINDING_DELETE_ROW, + KEY_BINDING_DUPLICATE_ROW, + KEY_BINDING_REVERT_INLINE_EDITOR_CHANGES, +} from '@cloudbeaver/plugin-data-spreadsheet-new'; import { KEY_BINDING_COLLAPSE_ALL, KEY_BINDING_ENABLE_FILTER, KEY_BINDING_LINK_OBJECT } from '@cloudbeaver/plugin-navigation-tree'; import { KEY_BINDING_SQL_EDITOR_EXECUTE, @@ -19,33 +25,21 @@ import { import type { IShortcut } from './IShortcut.js'; export const DATA_VIEWER_SHORTCUTS: IShortcut[] = [ - { - label: 'data_viewer_shortcut_start_inline_editing', - code: ['Enter', 'Backspace'], - }, { label: 'data_viewer_shortcut_revert_inline_editor_changes', - code: ['Escape'], + code: transformKeys(KEY_BINDING_REVERT_INLINE_EDITOR_CHANGES), }, { label: 'data_viewer_shortcut_add_new_row', - code: ['Alt + Insert'], + code: transformKeys(KEY_BINDING_ADD_NEW_ROW), }, { label: 'data_viewer_shortcut_duplicate_row', - code: ['Ctrl + Alt + Insert'], + code: transformKeys(KEY_BINDING_DUPLICATE_ROW), }, { label: 'data_viewer_shortcut_delete_row', - code: ['Delete'], - }, - { - label: 'data_viewer_shortcut_past_value', - code: ['Ctrl + V'], - }, - { - label: 'data_viewer_shortcut_copy_value', - code: ['Ctrl + C'], + code: transformKeys(KEY_BINDING_DELETE_ROW), }, ]; @@ -112,7 +106,7 @@ function transformModToDisplayKey(key: string): string { } if (OS === OperatingSystem.macOS) { - return key.replace('MOD', 'CMD').replace('ALT', 'OPTION').replace('BACKSPACE', 'DELETE'); + return key.replace('MOD', 'CMD').replace('ALT', 'OPTION'); } return key; } diff --git a/webapp/packages/plugin-help/tsconfig.json b/webapp/packages/plugin-help/tsconfig.json index dfcbdcad5d..310be66375 100644 --- a/webapp/packages/plugin-help/tsconfig.json +++ b/webapp/packages/plugin-help/tsconfig.json @@ -39,6 +39,9 @@ { "path": "../core-view/tsconfig.json" }, + { + "path": "../plugin-data-spreadsheet-new/tsconfig.json" + }, { "path": "../plugin-navigation-tree/tsconfig.json" }, diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/KEY_BINDING_ENABLE_FILTER.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/KEY_BINDING_ENABLE_FILTER.ts index 87bc7ef9b4..0c4457ba41 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/KEY_BINDING_ENABLE_FILTER.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/KEY_BINDING_ENABLE_FILTER.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_ENABLE_FILTER = createKeyBinding({ id: 'enable-filter', - keys: 'ctrl+f', + keys: 'mod+f', preventDefault: true, }); diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_COLLAPSE_ALL.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_COLLAPSE_ALL.ts index e033c0a8b5..f13032e36e 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_COLLAPSE_ALL.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_COLLAPSE_ALL.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_COLLAPSE_ALL = createKeyBinding({ id: 'collapse-all', - keys: 'ctrl+shift+/', + keys: 'shift+ctrl+/', preventDefault: true, }); diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_LINK_OBJECT.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_LINK_OBJECT.ts index 95221acf9a..8326bc42ca 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_LINK_OBJECT.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/KEY_BINDING_LINK_OBJECT.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_LINK_OBJECT = createKeyBinding({ id: 'link-object', - keys: 'ctrl+shift+,', + keys: 'shift+ctrl+,', preventDefault: true, }); diff --git a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE.ts b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE.ts index c6b7206847..3550f1d1e5 100644 --- a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE.ts +++ b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_SQL_EDITOR_EXECUTE = createKeyBinding({ id: 'sql-editor-execute', - keys: 'ctrl+enter', + keys: 'mod+enter', preventDefault: true, }); diff --git a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE_NEW.ts b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE_NEW.ts index 629e7a0986..e756300584 100644 --- a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE_NEW.ts +++ b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_EXECUTE_NEW.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_SQL_EDITOR_EXECUTE_NEW = createKeyBinding({ id: 'sql-editor-execute-new', - keys: ['ctrl+\\', 'shift+ctrl+enter'], + keys: ['mod+\\', 'shift+mod+enter'], preventDefault: true, }); diff --git a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_FORMAT.ts b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_FORMAT.ts index a591755a0b..44d878b73d 100644 --- a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_FORMAT.ts +++ b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_FORMAT.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_SQL_EDITOR_FORMAT = createKeyBinding({ id: 'sql-editor-format', - keys: 'shift+ctrl+f', + keys: 'shift+mod+f', preventDefault: true, }); diff --git a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN.ts b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN.ts index 36bdeb3438..db78f7743b 100644 --- a/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN.ts +++ b/webapp/packages/plugin-sql-editor/src/actions/bindings/KEY_BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN.ts @@ -9,6 +9,6 @@ import { createKeyBinding } from '@cloudbeaver/core-view'; export const KEY_BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN = createKeyBinding({ id: 'sql-editor-show-execution-plan', - keys: 'shift+ctrl+e', + keys: 'shift+mod+e', preventDefault: true, }); From db9df1f810390203265b5a07234e4b244cc2e493 Mon Sep 17 00:00:00 2001 From: alex <48489896+devnaumov@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:20:16 +0100 Subject: [PATCH 4/4] Cb 5831 refresh whole tree (#3129) * CB-5831 outdate children when refresh node * CB-5831 freeze resource until tree is constructed --------- Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> --- .../core-blocks/src/ResourcesHooks/useResource.ts | 7 +++++++ .../src/NodesManager/NavNodeManagerService.ts | 4 ++++ .../src/NodesManager/NavTreeResource.ts | 15 ++++++++++++++- .../src/ContextMenu/ConnectionMenuBootstrap.ts | 2 +- .../src/NavNodes/ResourceFoldersBootstrap.ts | 2 +- .../ElementsTree/useElementsTree.ts | 2 +- .../NavNodeMetadata/ObjectProperties.tsx | 9 +++++++-- .../src/ObjectViewerPanel/ObjectViewerPanel.tsx | 5 ++++- 8 files changed, 39 insertions(+), 7 deletions(-) diff --git a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts index 14b42e00d7..5b75716efc 100644 --- a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts +++ b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts @@ -47,6 +47,8 @@ type ResourceData, TKey, TInclud interface IActions, TKey, TIncludes> { active?: boolean; + /** Indicates whether the resource should be loadable without modifying data, unlike the "active" field */ + freeze?: boolean; forceSuspense?: boolean; silent?: boolean; onData?: (data: ResourceData, resource: TResource) => any; @@ -184,6 +186,7 @@ export function useResource< } return propertiesRef.resource.get(propertiesRef.key); } + return propertiesRef.resource.data; } @@ -290,6 +293,10 @@ export function useResource< () => ({ preloaded, get canLoad(): boolean { + if (actions?.freeze) { + return false; + } + return propertiesRef.key !== null && this.preloaded && this.outdated && !this.loading; }, get resource() { diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts index eedf491ba3..848f34cab8 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts @@ -194,6 +194,10 @@ export class NavNodeManagerService extends Bootstrap { await this.navTree.refreshTree(navNodeId); } + async refreshNode(navNodeId: string): Promise { + await this.navTree.refreshNode(navNodeId); + } + getTree(navNodeId: string): string[] | undefined; getTree(navNodeKey: NavNodeKey): string[] | undefined; getTree(navNodeKey: NavNodeKey[]): Array; diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts index fcccdafb0c..3f96e93972 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts @@ -150,6 +150,19 @@ export class NavTreeResource extends CachedMapResource { + this.performUpdate(navNodeId, [], async () => { + await this.graphQLService.sdk.navRefreshNode({ + nodePath: navNodeId, + }); + + if (!silent) { + this.markTreeOutdated(navNodeId); + } + await this.onNodeRefresh.execute(navNodeId); + }); + } + + async refreshNode(navNodeId: string, silent = false): Promise { this.performUpdate(navNodeId, [], async () => { await this.graphQLService.sdk.navRefreshNode({ nodePath: navNodeId, @@ -257,7 +270,7 @@ export class NavTreeResource extends CachedMapResource { diff --git a/webapp/packages/plugin-connections/src/ContextMenu/ConnectionMenuBootstrap.ts b/webapp/packages/plugin-connections/src/ContextMenu/ConnectionMenuBootstrap.ts index 27b802b921..6330328cd4 100644 --- a/webapp/packages/plugin-connections/src/ContextMenu/ConnectionMenuBootstrap.ts +++ b/webapp/packages/plugin-connections/src/ContextMenu/ConnectionMenuBootstrap.ts @@ -243,7 +243,7 @@ export class ConnectionMenuBootstrap extends Bootstrap { connection = await this.connectionInfoResource.changeConnectionView(createConnectionParam(connection), settings); if (connection.nodePath) { - await this.navNodeManagerService.refreshTree(connection.nodePath); + await this.navNodeManagerService.refreshNode(connection.nodePath); } } catch (exception: any) { this.notificationService.logException(exception); diff --git a/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts index 0a5d22b181..eb96149973 100644 --- a/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts +++ b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts @@ -223,7 +223,7 @@ export class ResourceFoldersBootstrap extends Bootstrap { const key = getRmResourcePath(result.projectId, result.folder ?? root); await this.resourceManagerResource.create(createPath(key, result.name), true); - this.navTreeResource.refreshTree(getRmProjectNodeId(result.projectId)); + this.navTreeResource.refreshNode(getRmProjectNodeId(result.projectId)); } catch (exception: any) { this.notificationService.logException(exception, 'Error occurred while renaming'); } diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts index 8f0e4f1e05..1b397c3e98 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts @@ -564,7 +564,7 @@ export function useElementsTree(options: IOptions): IElementsTree { }, async refresh(nodeId: string): Promise { try { - await navTreeResource.refreshTree(nodeId); + await navTreeResource.refreshNode(nodeId); } catch (exception: any) { notificationService.logException(exception, 'app_navigationTree_refresh_error'); } diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx index 4300babeb9..fa5ce5449a 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx @@ -17,7 +17,9 @@ import { useResource, useTranslate, } from '@cloudbeaver/core-blocks'; -import { DBObjectResource } from '@cloudbeaver/core-navigation-tree'; +import { useService } from '@cloudbeaver/core-di'; +import { DBObjectResource, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; +import { resourceKeyList } from '@cloudbeaver/core-resource'; import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; interface Props { @@ -28,7 +30,10 @@ const emptyArray: ObjectPropertyInfo[] = []; export const ObjectProperties = observer(function ObjectProperties({ objectId }) { const translate = useTranslate(); - const dbObject = useResource(ObjectProperties, DBObjectResource, objectId); + const navNodeInfoResource = useService(NavNodeInfoResource); + const dbObject = useResource(ObjectProperties, DBObjectResource, objectId, { + freeze: navNodeInfoResource.isOutdated(resourceKeyList(navNodeInfoResource.getParents(objectId))), + }); const { categories, isUncategorizedExists } = useObjectPropertyCategories(dbObject.data?.object?.properties ?? emptyArray); const properties = dbObject.data?.object?.properties; diff --git a/webapp/packages/plugin-object-viewer/src/ObjectViewerPanel/ObjectViewerPanel.tsx b/webapp/packages/plugin-object-viewer/src/ObjectViewerPanel/ObjectViewerPanel.tsx index 09bfa7f5dc..6137cd62a5 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectViewerPanel/ObjectViewerPanel.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectViewerPanel/ObjectViewerPanel.tsx @@ -11,7 +11,8 @@ import { observer } from 'mobx-react-lite'; import { getComputed, s, SContext, type StyleRegistry, TextPlaceholder, useResource, useS, useTranslate } from '@cloudbeaver/core-blocks'; import { ConnectionInfoResource } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; -import { NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; +import { NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree'; +import { resourceKeyList } from '@cloudbeaver/core-resource'; import { TabPanel, TabsBox, TabStyles, useTabLocalState } from '@cloudbeaver/core-ui'; import { MetadataMap } from '@cloudbeaver/core-utils'; import { ConnectionShieldLazy } from '@cloudbeaver/plugin-connections'; @@ -38,6 +39,7 @@ export const ObjectViewerPanel: TabHandlerPanelComponent const translate = useTranslate(); const dbObjectPagesService = useService(DBObjectPageService); const navNodeInfoResource = useService(NavNodeInfoResource); + const navTreeResource = useService(NavTreeResource); const innerTabState = useTabLocalState(() => new MetadataMap()); const style = useS(styles); @@ -53,6 +55,7 @@ export const ObjectViewerPanel: TabHandlerPanelComponent tab.handlerState.tabTitle = data.name; }); }, + freeze: navTreeResource.isOutdated(resourceKeyList(navNodeInfoResource.getParents(objectId))), active: getComputed(() => !!connection.tryGetData?.connected && !connection.outdated), });