diff --git a/packages/web/src/App.js b/packages/web/src/App.js index b8bd715de..0497767a6 100644 --- a/packages/web/src/App.js +++ b/packages/web/src/App.js @@ -28,18 +28,18 @@ function App() { - - - - - + + + + + - - - - - - + + + + + + diff --git a/packages/web/src/DragAndDropFileTarget.js b/packages/web/src/DragAndDropFileTarget.js index 41e0211f0..85ece2ec1 100644 --- a/packages/web/src/DragAndDropFileTarget.js +++ b/packages/web/src/DragAndDropFileTarget.js @@ -1,8 +1,8 @@ import React from 'react'; import styled from 'styled-components'; -import { fileformats } from './fileformats'; import { FontIcon } from './icons'; import useTheme from './theme/useTheme'; +import useExtensions from './utility/useExtensions'; const TargetStyled = styled.div` position: fixed; @@ -40,6 +40,7 @@ const TitleWrapper = styled.div` export default function DragAndDropFileTarget({ isDragActive, inputProps }) { const theme = useTheme(); + const { fileFormats } = useExtensions(); return ( !!isDragActive && ( @@ -50,7 +51,7 @@ export default function DragAndDropFileTarget({ isDragActive, inputProps }) { Drop the files to upload to DbGate Supported file types:{' '} - {fileformats + {fileFormats .filter((x) => x.readerFunc) .map((x) => x.name) .join(', ')} diff --git a/packages/web/src/appobj/databaseAppObject.js b/packages/web/src/appobj/databaseAppObject.js index ae4598f49..7e3db8f17 100644 --- a/packages/web/src/appobj/databaseAppObject.js +++ b/packages/web/src/appobj/databaseAppObject.js @@ -3,9 +3,9 @@ import _ from 'lodash'; import { DropDownMenuItem } from '../modals/DropDownMenu'; import { openNewTab } from '../utility/common'; import ImportExportModal from '../modals/ImportExportModal'; -import { defaultFileFormat } from '../fileformats'; +import { getDefaultFileFormat } from '../utility/fileformats'; -function Menu({ data, setOpenedTabs, showModal }) { +function Menu({ data, setOpenedTabs, showModal, extensions }) { const { connection, name } = data; const tooltip = `${connection.displayName || connection.server}\n${name}`; @@ -27,7 +27,7 @@ function Menu({ data, setOpenedTabs, showModal }) { { - const resp = await axios.get(`files/analyse-excel?filePath=${encodeURIComponent(file.full)}`); - const sheetNames = resp.data; - for (const sheetName of sheetNames) { - newSources.push(sheetName); - newValues[`sourceFile_${sheetName}`] = { - fileName: file.full, - sheetName, - }; - } - }, - - args: [ - { - type: 'checkbox', - name: 'singleFile', - label: 'Create single file', - direction: 'target', - }, - ], - - getDefaultOutputName: (sourceName, values) => { - if (values.target_excel_singleFile) { - return sourceName; - } - return null; - }, - - getOutputParams: (sourceName, values) => { - if (values.target_excel_singleFile) { - return { - sheetName: values[`targetName_${sourceName}`] || sourceName, - fileName:'data.xlsx' - }; - } - return null; - }, -}; - -export default excelFormat; diff --git a/packages/web/src/fileformats/index.ts b/packages/web/src/fileformats/index.ts deleted file mode 100644 index 2824490fe..000000000 --- a/packages/web/src/fileformats/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import csv from './csv'; -import jsonl from './jsonl'; -import excel from './excel'; -import { FileFormatDefinition } from './types'; - -export const fileformats = [csv, jsonl, excel]; - -export function findFileFormat(storageType): FileFormatDefinition { - return fileformats.find((x) => x.storageType == storageType); -} - -export function getFileFormatDirections(format: FileFormatDefinition) { - if (!format) return []; - const res = []; - if (format.readerFunc) res.push('source'); - if (format.writerFunc) res.push('target'); - return res; -} - -export const defaultFileFormat = csv; \ No newline at end of file diff --git a/packages/web/src/fileformats/jsonl.ts b/packages/web/src/fileformats/jsonl.ts deleted file mode 100644 index 944f5fef7..000000000 --- a/packages/web/src/fileformats/jsonl.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { FileFormatDefinition } from './types'; - -const jsonlFormat: FileFormatDefinition = { - storageType: 'jsonl', - extension: 'jsonl', - name: 'JSON lines', - readerFunc: 'jsonLinesReader', - writerFunc: 'jsonLinesWriter', -}; - -export default jsonlFormat; diff --git a/packages/web/src/fileformats/types.ts b/packages/web/src/fileformats/types.ts deleted file mode 100644 index c669c63fd..000000000 --- a/packages/web/src/fileformats/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface FileFormatDefinition { - storageType: string; - extension: string; - name: string; - readerFunc?: string; - writerFunc?: string; - args?: any[]; - addFilesToSourceList?: ( - file: { - full: string; - }, - newSources: string[], - newValues: { - [key: string]: any; - } - ) => void; - getDefaultOutputName?: (sourceName, values) => string; - getOutputParams?: (sourceName, values) => any; -} diff --git a/packages/web/src/impexp/ImportExportConfigurator.js b/packages/web/src/impexp/ImportExportConfigurator.js index ac137b503..6913ef286 100644 --- a/packages/web/src/impexp/ImportExportConfigurator.js +++ b/packages/web/src/impexp/ImportExportConfigurator.js @@ -12,10 +12,10 @@ import { FormArchiveFolderSelect, FormArchiveFilesSelect, } from '../utility/forms'; -import { useArchiveFiles, useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders'; +import { useArchiveFiles, useConnectionInfo, useDatabaseInfo, useInstalledPlugins } from '../utility/metadataLoaders'; import TableControl, { TableColumn } from '../utility/TableControl'; import { TextField, SelectField, CheckboxField } from '../utility/inputs'; -import { createPreviewReader, getActionOptions, getTargetName, isFileStorage } from './createImpExpScript'; +import { createPreviewReader, getActionOptions, getTargetName } from './createImpExpScript'; import getElectron from '../utility/getElectron'; import ErrorInfo from '../widgets/ErrorInfo'; import getAsArray from '../utility/getAsArray'; @@ -25,8 +25,9 @@ import SqlEditor from '../sqleditor/SqlEditor'; import { useUploadsProvider } from '../utility/UploadsProvider'; import { FontIcon } from '../icons'; import useTheme from '../theme/useTheme'; -import { fileformats, findFileFormat, getFileFormatDirections } from '../fileformats'; +import { findFileFormat, getFileFormatDirections } from '../utility/fileformats'; import FormArgumentList from '../utility/FormArgumentList'; +import useExtensions from '../utility/useExtensions'; const Container = styled.div` // max-height: 50vh; @@ -89,9 +90,9 @@ const Title = styled.div` margin: 10px 0px; `; -function getFileFilters(storageType) { +function getFileFilters(extensions, storageType) { const res = []; - const format = findFileFormat(storageType); + const format = findFileFormat(extensions, storageType); if (format) res.push({ name: format.name, extensions: [format.extension] }); res.push({ name: 'All Files', extensions: ['*'] }); return res; @@ -105,12 +106,12 @@ async function addFilesToSourceListDefault(file, newSources, newValues) { }; } -async function addFilesToSourceList(files, values, setValues, preferedStorageType, setPreviewSource) { +async function addFilesToSourceList(extensions, files, values, setValues, preferedStorageType, setPreviewSource) { const newSources = []; const newValues = {}; const storage = preferedStorageType || values.sourceStorageType; for (const file of getAsArray(files)) { - const format = findFileFormat(storage); + const format = findFileFormat(extensions, storage); if (format) { await (format.addFilesToSourceList || addFilesToSourceListDefault)(file, newSources, newValues); } @@ -132,17 +133,19 @@ function ElectronFilesInput() { const { values, setValues } = useFormikContext(); const electron = getElectron(); const [isLoading, setIsLoading] = React.useState(false); + const extensions = useExtensions(); const handleClick = async () => { const files = electron.remote.dialog.showOpenDialogSync(electron.remote.getCurrentWindow(), { properties: ['openFile', 'multiSelections'], - filters: getFileFilters(values.sourceStorageType), + filters: getFileFilters(extensions, values.sourceStorageType), }); if (files) { const path = window.require('path'); try { setIsLoading(true); await addFilesToSourceList( + extensions, files.map((full) => ({ full, ...path.parse(full), @@ -183,6 +186,7 @@ function SourceTargetConfig({ tablesField = undefined, engine = undefined, }) { + const extensions = useExtensions(); const theme = useTheme(); const { values, setFieldValue } = useFormikContext(); const types = @@ -190,7 +194,7 @@ function SourceTargetConfig({ ? [{ value: 'jsldata', label: 'Query result data', directions: ['source'] }] : [ { value: 'database', label: 'Database', directions: ['source', 'target'] }, - ...fileformats.map((format) => ({ + ...extensions.fileFormats.map((format) => ({ value: format.storageType, label: `${format.name} files(s)`, directions: getFileFormatDirections(format), @@ -201,7 +205,7 @@ function SourceTargetConfig({ const storageType = values[storageTypeField]; const dbinfo = useDatabaseInfo({ conid: values[connectionIdField], database: values[databaseNameField] }); const archiveFiles = useArchiveFiles({ folder: values[archiveFolderField] }); - const format = findFileFormat(storageType); + const format = findFileFormat(extensions, storageType); return ( {direction == 'source' && ( @@ -348,10 +352,17 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC const { setUploadListener } = useUploadsProvider(); const theme = useTheme(); const [previewSource, setPreviewSource] = React.useState(null); + const extensions = useExtensions(); + + console.log('ImportExportConfigurator', extensions); + const installed = useInstalledPlugins(); + console.log('installed', installed); const handleUpload = React.useCallback( (file) => { + console.log('UPLOAD', extensions); addFilesToSourceList( + extensions, [ { full: file.filePath, @@ -365,7 +376,7 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC ); // setFieldValue('sourceList', [...(sourceList || []), file.originalName]); }, - [setFieldValue, sourceList, values] + [extensions, setFieldValue, sourceList, values] ); React.useEffect(() => { @@ -381,11 +392,11 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC } }, []); - const supportsPreview = !!findFileFormat(values.sourceStorageType); + const supportsPreview = !!findFileFormat(extensions, values.sourceStorageType); const handleChangePreviewSource = async () => { if (previewSource && supportsPreview) { - const reader = await createPreviewReader(values, previewSource); + const reader = await createPreviewReader(extensions, values, previewSource); if (onChangePreview) onChangePreview(reader); } else { onChangePreview(null); @@ -444,8 +455,8 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC header="Action" formatter={(row) => ( setFieldValue(`actionType_${row}`, e.target.value)} /> )} @@ -455,7 +466,7 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC header="Target" formatter={(row) => ( setFieldValue(`targetName_${row}`, e.target.value)} /> )} diff --git a/packages/web/src/impexp/createImpExpScript.js b/packages/web/src/impexp/createImpExpScript.js index feb068550..d5826d4c3 100644 --- a/packages/web/src/impexp/createImpExpScript.js +++ b/packages/web/src/impexp/createImpExpScript.js @@ -4,12 +4,12 @@ import getAsArray from '../utility/getAsArray'; import { getConnectionInfo } from '../utility/metadataLoaders'; import engines from 'dbgate-engines'; import { findObjectLike } from 'dbgate-tools'; -import { findFileFormat } from '../fileformats'; +import { findFileFormat } from '../utility/fileformats'; -export function getTargetName(source, values) { +export function getTargetName(extensions, source, values) { const key = `targetName_${source}`; if (values[key]) return values[key]; - const format = findFileFormat(values.targetStorageType); + const format = findFileFormat(extensions, values.targetStorageType); if (format) { const res = format.getDefaultOutputName ? format.getDefaultOutputName(source, values) : null; if (res) return res; @@ -18,10 +18,6 @@ export function getTargetName(source, values) { return source; } -export function isFileStorage(storageType) { - return !!findFileFormat(storageType); -} - function extractApiParameters(values, direction, format) { const pairs = (format.args || []) .filter((arg) => arg.apiName) @@ -45,7 +41,7 @@ async function getConnection(storageType, conid, database) { return [null, null]; } -function getSourceExpr(sourceName, values, sourceConnection, sourceDriver) { +function getSourceExpr(extensions, sourceName, values, sourceConnection, sourceDriver) { const { sourceStorageType } = values; if (sourceStorageType == 'database') { const fullName = { schemaName: values.sourceSchemaName, pureName: sourceName }; @@ -66,9 +62,9 @@ function getSourceExpr(sourceName, values, sourceConnection, sourceDriver) { }, ]; } - if (isFileStorage(sourceStorageType)) { + if (findFileFormat(extensions, sourceStorageType)) { const sourceFile = values[`sourceFile_${sourceName}`]; - const format = findFileFormat(sourceStorageType); + const format = findFileFormat(extensions, sourceStorageType); if (format && format.readerFunc) { return [ format.readerFunc, @@ -113,9 +109,9 @@ function getFlagsFroAction(action) { }; } -function getTargetExpr(sourceName, values, targetConnection, targetDriver) { +function getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver) { const { targetStorageType } = values; - const format = findFileFormat(targetStorageType); + const format = findFileFormat(extensions, targetStorageType); if (format && format.writerFunc) { const outputParams = format.getOutputParams && format.getOutputParams(sourceName, values); return [ @@ -124,7 +120,7 @@ function getTargetExpr(sourceName, values, targetConnection, targetDriver) { ...(outputParams ? outputParams : { - fileName: getTargetName(sourceName, values), + fileName: getTargetName(extensions, sourceName, values), }), ...extractApiParameters(values, 'target', format), }, @@ -136,7 +132,7 @@ function getTargetExpr(sourceName, values, targetConnection, targetDriver) { { connection: targetConnection, schemaName: values.targetSchemaName, - pureName: getTargetName(sourceName, values), + pureName: getTargetName(extensions, sourceName, values), ...getFlagsFroAction(values[`actionType_${sourceName}`]), }, ]; @@ -146,7 +142,7 @@ function getTargetExpr(sourceName, values, targetConnection, targetDriver) { 'archiveWriter', { folderName: values.targetArchiveFolder, - fileName: getTargetName(sourceName, values), + fileName: getTargetName(extensions, sourceName, values), }, ]; } @@ -154,7 +150,7 @@ function getTargetExpr(sourceName, values, targetConnection, targetDriver) { throw new Error(`Unknown target storage type: ${targetStorageType}`); } -export default async function createImpExpScript(values, addEditorInfo = true) { +export default async function createImpExpScript(extensions, values, addEditorInfo = true) { const script = new ScriptWriter(); const [sourceConnection, sourceDriver] = await getConnection( @@ -172,11 +168,11 @@ export default async function createImpExpScript(values, addEditorInfo = true) { for (const sourceName of sourceList) { const sourceVar = script.allocVariable(); // @ts-ignore - script.assign(sourceVar, ...getSourceExpr(sourceName, values, sourceConnection, sourceDriver)); + script.assign(sourceVar, ...getSourceExpr(extensions, sourceName, values, sourceConnection, sourceDriver)); const targetVar = script.allocVariable(); // @ts-ignore - script.assign(targetVar, ...getTargetExpr(sourceName, values, targetConnection, targetDriver)); + script.assign(targetVar, ...getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver)); script.copyStream(sourceVar, targetVar); script.put(); @@ -188,9 +184,9 @@ export default async function createImpExpScript(values, addEditorInfo = true) { return script.s; } -export function getActionOptions(source, values, targetDbinfo) { +export function getActionOptions(extensions, source, values, targetDbinfo) { const res = []; - const targetName = getTargetName(source, values); + const targetName = getTargetName(extensions, source, values); if (values.targetStorageType == 'database') { let existing = findObjectLike( { schemaName: values.targetSchemaName, pureName: targetName }, @@ -225,13 +221,13 @@ export function getActionOptions(source, values, targetDbinfo) { return res; } -export async function createPreviewReader(values, sourceName) { +export async function createPreviewReader(extensions, values, sourceName) { const [sourceConnection, sourceDriver] = await getConnection( values.sourceStorageType, values.sourceConnectionId, values.sourceDatabaseName ); - const [functionName, props] = getSourceExpr(sourceName, values, sourceConnection, sourceDriver); + const [functionName, props] = getSourceExpr(extensions, sourceName, values, sourceConnection, sourceDriver); return { functionName, props: { diff --git a/packages/web/src/modals/ImportExportModal.js b/packages/web/src/modals/ImportExportModal.js index bd62384d8..4408724aa 100644 --- a/packages/web/src/modals/ImportExportModal.js +++ b/packages/web/src/modals/ImportExportModal.js @@ -22,7 +22,8 @@ import useSocket from '../utility/SocketProvider'; import LoadingInfo from '../widgets/LoadingInfo'; import { FontIcon } from '../icons'; import LargeButton from '../widgets/LargeButton'; -import { defaultFileFormat } from '../fileformats'; +import { getDefaultFileFormat } from '../utility/fileformats'; +import useExtensions from '../utility/useExtensions'; const headerHeight = '60px'; const footerHeight = '100px'; @@ -92,9 +93,10 @@ const FooterButtons = styled.div` function GenerateSctriptButton({ modalState }) { const setOpenedTabs = useSetOpenedTabs(); const { values } = useFormikContext(); + const extensions = useExtensions(); const handleGenerateScript = async () => { - const code = await createImpExpScript(values); + const code = await createImpExpScript(extensions, values); openNewTab(setOpenedTabs, { title: 'Shell', icon: 'img shell', @@ -141,6 +143,7 @@ export default function ImportExportModal({ const refreshArchiveFolderRef = React.useRef(null); const setArchive = useSetCurrentArchive(); const setCurrentWidget = useSetCurrentWidget(); + const extensions = useExtensions(); const [busy, setBusy] = React.useState(false); @@ -165,9 +168,9 @@ export default function ImportExportModal({ const handleExecute = async (values) => { if (busy) return; - + setBusy(true); - const script = await createImpExpScript(values); + const script = await createImpExpScript(extensions, values); setExecuteNumber((num) => num + 1); @@ -194,7 +197,7 @@ export default function ImportExportModal({ onSubmit={handleExecute} initialValues={{ sourceStorageType: 'database', - targetStorageType: importToArchive ? 'archive' : defaultFileFormat.storageType, + targetStorageType: importToArchive ? 'archive' : getDefaultFileFormat(extensions).storageType, sourceArchiveFolder: archive, targetArchiveFolder, ...initialValues, diff --git a/packages/web/src/plugins/PluginsProvider.js b/packages/web/src/plugins/PluginsProvider.js index 7ae3fa25d..6e9f5767d 100644 --- a/packages/web/src/plugins/PluginsProvider.js +++ b/packages/web/src/plugins/PluginsProvider.js @@ -9,12 +9,7 @@ export default function PluginsProvider({ children }) { const installedPlugins = useInstalledPlugins(); const [plugins, setPlugins] = React.useState({}); const handleLoadPlugins = async () => { - setPlugins((x) => - _.pick( - x, - installedPlugins.map((y) => y.name) - ) - ); + const newPlugins = {}; for (const installed of installedPlugins) { if (!_.keys(plugins).includes(installed.name)) { console.log('Loading module', installed.name); @@ -26,12 +21,16 @@ export default function PluginsProvider({ children }) { }, }); const module = eval(resp.data); - setPlugins((v) => ({ - ...v, - [installed.name]: module, - })); + console.log('Loaded plugin', module); + newPlugins[installed.name] = module.__esModule ? module.default : module; } } + setPlugins((x) => + _.pick( + { ...x, ...newPlugins }, + installedPlugins.map((y) => y.name) + ) + ); }; React.useEffect(() => { handleLoadPlugins(); @@ -42,11 +41,16 @@ export default function PluginsProvider({ children }) { export function usePlugins() { const installed = useInstalledPlugins(); const loaded = React.useContext(PluginsContext); - return installed - .map((manifest) => ({ - packageName: manifest.name, - manifest, - content: loaded[manifest.name], - })) - .filter((x) => x.content); + + return React.useMemo( + () => + installed + .map((manifest) => ({ + packageName: manifest.name, + manifest, + content: loaded[manifest.name], + })) + .filter((x) => x.content), + [installed, loaded] + ); } diff --git a/packages/web/src/utility/UploadsProvider.js b/packages/web/src/utility/UploadsProvider.js index 81a5de7d2..f75ab1b8f 100644 --- a/packages/web/src/utility/UploadsProvider.js +++ b/packages/web/src/utility/UploadsProvider.js @@ -1,9 +1,10 @@ import React from 'react'; import { useDropzone } from 'react-dropzone'; -import { findFileFormat } from '../fileformats'; import ImportExportModal from '../modals/ImportExportModal'; import useShowModal from '../modals/showModal'; +import { findFileFormat } from './fileformats'; import resolveApi from './resolveApi'; +import useExtensions from './useExtensions'; const UploadsContext = React.createContext(null); @@ -19,6 +20,7 @@ export function useUploadsProvider() { export function useUploadsZone() { const { uploadListener } = useUploadsProvider(); const showModal = useShowModal(); + const extensions = useExtensions(); const onDrop = React.useCallback( (files) => { @@ -43,7 +45,7 @@ export function useUploadsZone() { if (uploadListener) { uploadListener(fileData); } else { - if (findFileFormat(fileData.storageType)) { + if (findFileFormat(extensions, fileData.storageType)) { showModal((modalState) => ( { + const resp = await axios.get(`files/analyse-excel?filePath=${encodeURIComponent(file.full)}`); + const sheetNames = resp.data; + for (const sheetName of sheetNames) { + newSources.push(sheetName); + newValues[`sourceFile_${sheetName}`] = { + fileName: file.full, + sheetName, + }; + } + }, + + args: [ + { + type: 'checkbox', + name: 'singleFile', + label: 'Create single file', + direction: 'target', + }, + ], + + getDefaultOutputName: (sourceName, values) => { + if (values.target_excel_singleFile) { + return sourceName; + } + return null; + }, + + getOutputParams: (sourceName, values) => { + if (values.target_excel_singleFile) { + return { + sheetName: values[`targetName_${sourceName}`] || sourceName, + fileName: 'data.xlsx', + }; + } + return null; + }, +}; + +const jsonlFormat = { + storageType: 'jsonl', + extension: 'jsonl', + name: 'JSON lines', + readerFunc: 'jsonLinesReader', + writerFunc: 'jsonLinesWriter', +}; + +export function buildFileFormats(plugins) { + const res = [excelFormat, jsonlFormat]; + for (const { content } of plugins) { + const { fileFormats } = content; + if (fileFormats) res.push(...fileFormats); + } + return res; +} + +export function findFileFormat(extensions, storageType) { + return extensions.fileFormats.find((x) => x.storageType == storageType); +} + +export function getFileFormatDirections(format) { + if (!format) return []; + const res = []; + if (format.readerFunc) res.push('source'); + if (format.writerFunc) res.push('target'); + return res; +} + +export function getDefaultFileFormat(extensions) { + return extensions.fileFormats.find((x) => x.storageType == 'csv') || jsonlFormat; } diff --git a/packages/web/src/utility/globalState.js b/packages/web/src/utility/globalState.js index 9a5f576f8..51ede5906 100644 --- a/packages/web/src/utility/globalState.js +++ b/packages/web/src/utility/globalState.js @@ -5,6 +5,7 @@ import { useConnectionInfo, useConfig } from './metadataLoaders'; import usePrevious from './usePrevious'; import useNewQuery from '../query/useNewQuery'; import useShowModal from '../modals/showModal'; +import useExtensions from './useExtensions'; function createGlobalState(defaultValue) { const Context = React.createContext(null); @@ -90,6 +91,7 @@ export function useAppObjectParams() { const currentArchive = useCurrentArchive(); const showModal = useShowModal(); const config = useConfig(); + const extensions = useExtensions(); return { setOpenedTabs, diff --git a/packages/web/src/utility/metadataLoaders.js b/packages/web/src/utility/metadataLoaders.js index 5ac8b597e..ffe8fc6c9 100644 --- a/packages/web/src/utility/metadataLoaders.js +++ b/packages/web/src/utility/metadataLoaders.js @@ -88,7 +88,7 @@ const connectionListLoader = () => ({ reloadTrigger: `connection-list-changed`, }); -const insttalledPluginsLoader = () => ({ +const installedPluginsLoader = () => ({ url: 'plugins/installed', params: {}, reloadTrigger: `installed-plugins-changed`, @@ -251,8 +251,8 @@ export function useArchiveFolders(args) { } export function getInstalledPlugins(args) { - return getCore(insttalledPluginsLoader, args) || []; + return getCore(installedPluginsLoader, args) || []; } export function useInstalledPlugins(args) { - return useCore(insttalledPluginsLoader, args) || []; + return useCore(installedPluginsLoader, args) || []; } diff --git a/packages/web/src/utility/useExtensions.js b/packages/web/src/utility/useExtensions.js new file mode 100644 index 000000000..6b53996c7 --- /dev/null +++ b/packages/web/src/utility/useExtensions.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { usePlugins } from '../plugins/PluginsProvider'; +import { buildFileFormats } from './fileformats'; + +export default function useExtensions() { + const plugins = usePlugins(); + const extensions = React.useMemo( + () => ({ + plugins, + fileFormats: buildFileFormats(plugins), + }), + [plugins] + ); + return extensions; +} diff --git a/packages/web/src/widgets/Toolbar.js b/packages/web/src/widgets/Toolbar.js index d0c91089d..0c3aade65 100644 --- a/packages/web/src/widgets/Toolbar.js +++ b/packages/web/src/widgets/Toolbar.js @@ -10,7 +10,8 @@ import { openNewTab } from '../utility/common'; import useNewFreeTable from '../freetable/useNewFreeTable'; import ImportExportModal from '../modals/ImportExportModal'; import useShowModal from '../modals/showModal'; -import { defaultFileFormat } from '../fileformats'; +import useExtensions from '../utility/useExtensions'; +import { getDefaultFileFormat } from '../utility/fileformats'; const ToolbarContainer = styled.div` display: flex; @@ -28,6 +29,7 @@ export default function ToolBar({ toolbarPortalRef }) { const showModal = useShowModal(); const currentTheme = useCurrentTheme(); const setCurrentTheme = useSetCurrentTheme(); + const extensions = useExtensions(); React.useEffect(() => { window['dbgate_createNewConnection'] = modalState.open; @@ -41,7 +43,7 @@ export default function ToolBar({ toolbarPortalRef }) { modalState={modalState} importToArchive initialValues={{ - sourceStorageType: defaultFileFormat.storageType, + sourceStorageType: getDefaultFileFormat(extensions).storageType, // sourceConnectionId: data.conid, // sourceDatabaseName: data.database, // sourceSchemaName: data.schemaName,