Compare commits

...
Author SHA1 Message Date
Sychev AndreyandDaria Marutkina b636577a23 dbeaver/pro#7810 fix: improve submenu visibility handling for lazy-lo… (#3993) (#3996)
* dbeaver/pro#7810 fix: improve submenu visibility handling for lazy-loaded items

* dbeaver/pro#7810 fix: return back isDisabled for handler

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-12-18 11:22:20 +01:00
sergeyteleshevandSychev Andrey 21c772f281 dbeaver/pro#7811 feat: fix file selection for uploads by removing focus and visibility listeners (#3992) (#3994)
Co-authored-by: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com>
2025-12-17 16:58:53 +01:00
5 changed files with 40 additions and 38 deletions
@@ -31,9 +31,16 @@ export function promptForFiles({ multiple = false, accept }: { multiple?: boolea
let settled = false;
const SAFETY_TIMEOUT_MS = 10 * 60 * 1000;
const safetyTimeoutId = setTimeout(() => {
if (!settled) {
settleErr(new Error('File selection timed out'));
}
}, SAFETY_TIMEOUT_MS);
function cleanup() {
window.removeEventListener('focus', onWindowFocus, true);
document.removeEventListener('visibilitychange', onVisibilityChange, true);
window.clearTimeout(safetyTimeoutId);
if (input.isConnected) {
document.body.removeChild(input);
}
@@ -63,25 +70,9 @@ export function promptForFiles({ multiple = false, accept }: { multiple?: boolea
}
});
function afterPickerClosedCheck() {
setTimeout(() => {
if (!settled) {
settleErr(new Error('No files selected'));
}
}, 1000);
}
function onWindowFocus() {
afterPickerClosedCheck();
}
function onVisibilityChange() {
if (document.visibilityState === 'visible') {
afterPickerClosedCheck();
}
}
window.addEventListener('focus', onWindowFocus, true);
document.addEventListener('visibilitychange', onVisibilityChange, true);
input.addEventListener('cancel', () => {
settleOk([]);
});
document.body.appendChild(input);
@@ -90,12 +81,5 @@ export function promptForFiles({ multiple = false, accept }: { multiple?: boolea
} catch (err) {
settleErr(err instanceof Error ? err : new Error('File dialog blocked'));
}
const SAFETY_TIMEOUT_MS = 10 * 60 * 1000;
setTimeout(() => {
if (!settled) {
settleErr(new Error('File selection timed out'));
}
}, SAFETY_TIMEOUT_MS);
});
}
@@ -68,8 +68,18 @@ export const SubMenuElement = observer<ISubMenuElementProps>(function SubMenuEle
const handler = subMenuData.handler;
// TODO: it's better to remove this expensive check to allow lazy loading of menu items
const hidden = getComputed(() => subMenuData.items.every(item => item.hidden) || handler?.isHidden?.(subMenuData.context));
// NOTE: some menus rely on lazy loading. When items are not loaded yet `items` may be empty,
// but the submenu still must be rendered to allow opening it and triggering loaders.
const hidden = getComputed(() => {
if (handler?.isHidden?.(subMenuData.context)) {
return true;
}
const hasVisibleItems = subMenuData.items.some(item => !item.hidden);
const canLazyLoadItems = subMenuData.loaders.length > 0;
return !hasVisibleItems && !canLazyLoadItems;
});
const IconComponent = handler?.iconComponent?.() ?? subMenu.iconComponent?.();
const extraProps = handler?.getExtraProps?.() ?? (subMenu.getExtraProps?.() as any);
// TODO: fix, this triggers `Cannot update a component (`SubMenuElement`) while rendering a different component`
@@ -100,12 +100,16 @@ export class DataGridContextMenuSaveContentService {
}
if (action === ACTION_UPLOAD) {
promptForFiles().then(files => {
try {
const files = await promptForFiles();
const file = files?.[0] ?? undefined;
if (file) {
edit.set(key, createResultSetBlobValue(file));
}
});
} catch (exception: any) {
this.notificationService.logException(exception, 'ui_upload_file_fail');
}
}
},
});
@@ -102,7 +102,7 @@ export function useValuePanelImageValue({ model, resultIndex }: Props) {
return this.contentAction.isDownloadable(this.cellHolder);
}
return this.staticSrc && !this.truncated;
return !!this.staticSrc && !this.truncated;
},
get canUpload() {
if (!this.cellHolder) {
@@ -115,7 +115,7 @@ export function useValuePanelImageValue({ model, resultIndex }: Props) {
return false;
}
return this.cellHolder && this.contentAction.isBlobTruncated(this.cellHolder);
return !!this.cellHolder && this.contentAction.isBlobTruncated(this.cellHolder);
},
async download() {
try {
@@ -134,13 +134,17 @@ export function useValuePanelImageValue({ model, resultIndex }: Props) {
this.notificationService.logException(exception, 'data_viewer_presentation_value_content_download_error');
}
},
upload() {
promptForFiles().then(files => {
async upload() {
try {
const files = await promptForFiles();
const file = files?.[0];
if (file && this.selectedCell) {
this.editAction.set(this.selectedCell, createResultSetBlobValue(file));
}
});
} catch (exception: any) {
this.notificationService.logException(exception, 'ui_upload_file_fail');
}
},
async loadFullImage() {
if (!this.selectedCell) {
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.