diff --git a/packages/tools/src/dbKeysLoader.ts b/packages/tools/src/dbKeysLoader.ts new file mode 100644 index 000000000..aafb591c4 --- /dev/null +++ b/packages/tools/src/dbKeysLoader.ts @@ -0,0 +1,177 @@ +import _omit from 'lodash/omit'; + +const SHOW_INCREMENT = 100; + +export interface DbKeysNodeModelBase { + text?: string; + count?: number; + level: number; +} + +export interface DbKeysLeafNodeModel extends DbKeysNodeModelBase { + key: string; + + type: 'string' | 'hash' | 'set' | 'list' | 'zset' | 'stream' | 'binary' | 'ReJSON-RL'; +} + +export interface DbKeysFolderNodeModel extends DbKeysNodeModelBase { + root: string; + type: 'dir'; + maxShowCount?: number; + isExpanded?: boolean; + shouldLoadNext?: boolean; + hasNext?: boolean; +} + +export interface DbKeysTreeModel { + root: DbKeysFolderNodeModel; + dirsByKey: { [key: string]: DbKeysFolderNodeModel }; + childrenByKey: { [key: string]: DbKeysNodeModel[] }; + refreshAll?: boolean; +} + +export type DbKeysNodeModel = DbKeysLeafNodeModel | DbKeysFolderNodeModel; + +export type DbKeysLoadFunction = (root: string, limit: number) => Promise; + +export type DbKeysChangeModelFunction = (func: (model: DbKeysTreeModel) => DbKeysTreeModel) => void; + +// function dbKeys_findFolderNode(node: DbKeysNodeModel, root: string) { +// if (node.type != 'dir') { +// return null; +// } +// if (node.root === root) { +// return node; +// } +// for (const child of node.children ?? []) { +// const res = dbKeys_findFolderNode(child, root); +// if (res) { +// return res; +// } +// } +// return null; +// } + +// export async function dbKeys_loadKeysFromNode( +// tree: DbKeysTreeModel, +// callingRoot: string, +// separator: string, +// loader: DbKeysLoadFunction +// ): Promise { +// const callingRootNode = tree.dirsByKey[callingRoot]; +// if (!callingRootNode) { +// return tree; +// } +// const newItems = await loader(callingRoot, callingRootNode.maxShowCount ?? SHOW_INCREMENT); + +// return { +// ...tree, +// childrenByKey: { +// ...tree.childrenByKey, +// [callingRoot]: newItems, +// }, +// }; +// } + +export async function dbKeys_loadMissing(tree: DbKeysTreeModel, loader: DbKeysLoadFunction): Promise { + const childrenByKey = { ...tree.childrenByKey }; + const dirsByKey = { ...tree.dirsByKey }; + + for (const root in tree.dirsByKey) { + const dir = tree.dirsByKey[root]; + + if (dir.isExpanded && dir.shouldLoadNext) { + if (!tree.childrenByKey[root] || dir.hasNext) { + const loadCount = dir.maxShowCount && dir.shouldLoadNext ? dir.maxShowCount + SHOW_INCREMENT : SHOW_INCREMENT; + const items = await loader(root, loadCount + 1); + + childrenByKey[root] = items.slice(0, loadCount); + dirsByKey[root] = { + ...dir, + shouldLoadNext: false, + maxShowCount: loadCount, + hasNext: items.length > loadCount, + }; + + for (const child of items.slice(0, loadCount)) { + if (child.type == 'dir' && !dirsByKey[child.root]) { + dirsByKey[child.root] = { + shouldLoadNext: false, + maxShowCount: null, + hasNext: false, + isExpanded: false, + type: 'dir', + level: dir.level + 1, + root: child.root, + text: child.text, + }; + } + } + } else { + dirsByKey[root] = { + ...dir, + shouldLoadNext: false, + }; + } + } + } + + return { + ...tree, + dirsByKey, + childrenByKey, + refreshAll: false, + }; +} + +export function dbKeys_markNodeExpanded(tree: DbKeysTreeModel, root: string, isExpanded: boolean): DbKeysTreeModel { + const node = tree.dirsByKey[root]; + if (!node) { + return tree; + } + return { + ...tree, + dirsByKey: { + ...tree.dirsByKey, + [root]: { + ...node, + isExpanded, + shouldLoadNext: isExpanded, + }, + }, + }; +} + +export function dbKeys_refreshAll(tree?: DbKeysTreeModel): DbKeysTreeModel { + const root: DbKeysFolderNodeModel = { + isExpanded: true, + level: 0, + root: '', + type: 'dir', + shouldLoadNext: true, + }; + return { + ...tree, + childrenByKey: {}, + dirsByKey: { + '': root, + }, + refreshAll: true, + root, + }; +} + +export function dbKeys_reloadFolder(tree: DbKeysTreeModel, root: string): DbKeysTreeModel { + return { + ...tree, + childrenByKey: _omit(tree.childrenByKey, root), + dirsByKey: { + ...tree.dirsByKey, + [root]: { + ...tree.dirsByKey[root], + shouldLoadNext: true, + hasNext: undefined, + }, + }, + }; +} diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts index 46b6c36f0..b446d0fd9 100644 --- a/packages/tools/src/index.ts +++ b/packages/tools/src/index.ts @@ -24,3 +24,4 @@ export * from './getConnectionLabel'; export * from './detectSqlFilterBehaviour'; export * from './filterBehaviours'; export * from './schemaInfoTools'; +export * from './dbKeysLoader'; diff --git a/packages/web/src/widgets/DbKeysSubTree.svelte b/packages/web/src/widgets/DbKeysSubTree.svelte index e82d470b3..5138474f2 100644 --- a/packages/web/src/widgets/DbKeysSubTree.svelte +++ b/packages/web/src/widgets/DbKeysSubTree.svelte @@ -2,89 +2,38 @@ import _ from 'lodash'; import AppObjectCore from '../appobj/AppObjectCore.svelte'; - import LoadingInfo from '../elements/LoadingInfo.svelte'; - import { apiCall } from '../utility/api'; - - const SHOW_INCREMENT = 100; import DbKeysTreeNode from './DbKeysTreeNode.svelte'; - - export let conid; - export let database; + import { dbKeys_markNodeExpanded, DbKeysChangeModelFunction, DbKeysTreeModel } from 'dbgate-tools'; export let root; + export let connection; + export let database; + export let conid; export let indentLevel = 0; - export let reloadToken = 0; - export let connection; export let filter; - let reloadToken2 = 0; - let maxShowCount = SHOW_INCREMENT; - let loading = false; - let loadingWhole = false; - let items = []; + export let model: DbKeysTreeModel; + export let changeModel: DbKeysChangeModelFunction; - async function loadData() { - loading = true; - const result = await apiCall('database-connections/load-keys', { - conid, - database, - root, - filter, - limit: maxShowCount + 1, - }); - items = result; - loading = false; - loadingWhole = false; - } - - $: { - conid; - database; - root; - filter; - reloadToken; - reloadToken2; - maxShowCount; - loadData(); - } - - $: { - reloadToken; - loadingWhole = true; - } + $: items = model.childrenByKey[root] ?? []; -{#if loadingWhole} - -{:else} - {#each items.slice(0, maxShowCount) as item} - { - reloadToken2 += 1; - }} - /> - {/each} +{#each items as item} + +{/each} - {#if loading} - - {:else if items.length > maxShowCount} - { - maxShowCount += SHOW_INCREMENT; - }} - /> - {/if} +{#if model.dirsByKey[root]?.shouldLoadNext} + +{:else if model.dirsByKey[root]?.hasNext} + { + changeModel(model => dbKeys_markNodeExpanded(model, root, true)); + }} + /> {/if} diff --git a/packages/web/src/widgets/DbKeysTree.svelte b/packages/web/src/widgets/DbKeysTree.svelte index 27ba2f843..c13f07faa 100644 --- a/packages/web/src/widgets/DbKeysTree.svelte +++ b/packages/web/src/widgets/DbKeysTree.svelte @@ -1,9 +1,8 @@ @@ -74,5 +99,5 @@ {/if} - + diff --git a/packages/web/src/widgets/DbKeysTreeNode.svelte b/packages/web/src/widgets/DbKeysTreeNode.svelte index 0c666ff47..7de1a7e06 100644 --- a/packages/web/src/widgets/DbKeysTreeNode.svelte +++ b/packages/web/src/widgets/DbKeysTreeNode.svelte @@ -1,5 +1,11 @@