From 25a691a54aad4c5467d2b2164bf6eb68c0f9902c Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Wed, 19 Jul 2023 15:26:32 +0200 Subject: [PATCH 01/10] Add import-export options for sidebar filters Add ability to import and export all filters, Charts from sidebar. At the moment user can still export all filters from history in toolbar but to enhance the accessibility this PR add same feature in sidebar. Fixes: #1833 --- .../app/ui/views/sidebar/search/component.ts | 4 ++-- .../sidebar/search/providers/providers.ts | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/component.ts b/application/client/src/app/ui/views/sidebar/search/component.ts index 9ec1046b0b..7e26b58234 100644 --- a/application/client/src/app/ui/views/sidebar/search/component.ts +++ b/application/client/src/app/ui/views/sidebar/search/component.ts @@ -59,10 +59,10 @@ export class Filters extends ChangesDetector implements OnDestroy, AfterContentI handler: () => { this.log().debug(`Not implemented yet`); }, - }, + } ]; contextmenu.show({ - items: items, + items: this.providers.contextMenuOptions(items), x: event.pageX, y: event.pageY, }); diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index a18c4f99b6..17be3061d7 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -485,6 +485,14 @@ export class Providers { }); }, }); + + event.items.push({ /* Delimiter */}, { + caption: 'Export All to File', + handler: () => console.log('Exporting to the file'), + }); + + event.items.push({ caption: 'Import from File', handler: () => console.log('Importing from file') }); + this._providers.forEach((provider: Provider) => { const custom: IMenuItem[] = provider.getContextMenuItems( event.entity, @@ -500,6 +508,21 @@ export class Providers { this.subjects.get().context.emit(event); } + public contextMenuOptions(items: IMenuItem[]): IMenuItem[] { + items.push({}); + items.push( + { + caption: 'Export All to File', + handler: () => console.log('Exporting from providers'), + }); + items.push( + { + caption: 'Import from File', + handler: () => console.log('Importing'), + }); + return items; + } + private _onDoubleclickEvent(event: IDoubleclickEvent) { event.provider.search(event.entity); } From bcac87893c2d6c8f76d57a69cb600899327bbb7a Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Wed, 26 Jul 2023 14:02:18 +0200 Subject: [PATCH 02/10] Add import/export handler function --- application/client/src/app/service/history.ts | 2 +- .../src/app/service/history/provider.ts | 5 ++- .../sidebar/search/providers/providers.ts | 44 ++++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/application/client/src/app/service/history.ts b/application/client/src/app/service/history.ts index f21782f084..143519ef59 100644 --- a/application/client/src/app/service/history.ts +++ b/application/client/src/app/service/history.ts @@ -76,7 +76,7 @@ export class Service extends Implementation { return this.provider.export(uuids, filename); } - public import(filename: string): Promise { + public import(filename: string): Promise { return this.provider.import(filename); } } diff --git a/application/client/src/app/service/history/provider.ts b/application/client/src/app/service/history/provider.ts index 2b42ffdb29..0ba030b193 100644 --- a/application/client/src/app/service/history/provider.ts +++ b/application/client/src/app/service/history/provider.ts @@ -58,7 +58,7 @@ export class Provider implements EntryConvertable { return bridge.entries({ file: filename }).overwrite([this.entry().to()]); } - public import(filename: string): Promise { + public import(filename: string): Promise { return bridge .entries({ file: filename }) .get() @@ -83,11 +83,12 @@ export class Provider implements EntryConvertable { }); return col; }); + let uuid: string[] = this.collections.map(collection => collection.uuid); this.storage.definitions.add(this.definitions); this.storage.collections.add(this.collections); this.collections = []; this.definitions = []; - return undefined; + return uuid; }); } diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index 17be3061d7..0efbf33138 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -15,6 +15,8 @@ import { Entity } from './definitions/entity'; import { unique } from '@platform/env/sequence'; import { Logger } from '@platform/log'; import { ProvidersEvents } from './definitions/events'; +import { history } from '@service/history'; +import { bridge } from '@service/bridge'; type TSelectedEntities = string[]; @@ -508,17 +510,55 @@ export class Providers { this.subjects.get().context.emit(event); } + public contextMenuOptions(items: IMenuItem[]): IMenuItem[] { + const historySession = history.get(this.session); + if(historySession === undefined) { + this.logger.error('History session is not defined'); + return items + } items.push({}); items.push( { caption: 'Export All to File', - handler: () => console.log('Exporting from providers'), + handler: () => { + bridge.files().select.save().then((filename: string | undefined) => { + if(filename === undefined) { + return; + } + history.export([historySession.collections.uuid], filename); + }).catch(error => this.logger.error(error.message)); + }, }); items.push( { caption: 'Import from File', - handler: () => console.log('Importing'), + handler: () => { + bridge.files().select.text() + .then(file => { + if (file.length !== 1) { + this.logger.error('No file selected'); + return; + } + history.import(file[0].filename).then((uuids: string[]) => { + if (uuids.length === 0) { + this.logger.warn('File does not have collection'); + return; + } + if (uuids.length > 1) { + this.session.switch().toolbar.presets(); + return; + } else { + const collection = history.collections.get(uuids[0]); + if (collection === undefined) { + this.logger.error(`Cannot find imported collection with UUID: ${uuids[0]}`); + return; + } + historySession.apply(collection); + } + }); + }); + }, }); return items; } From 339b6bd5ef7989367ee9cdbb76437d3b2d9dbb96 Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Thu, 27 Jul 2023 11:19:27 +0200 Subject: [PATCH 03/10] Show the export option only when filters are present --- .../src/app/ui/views/sidebar/search/providers/providers.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index 0efbf33138..46d5ba4c18 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -518,7 +518,9 @@ export class Providers { return items } items.push({}); - items.push( + let store = this.session.search.store(); + let showExport: boolean = (store.filters().get().length + store.charts().get().length + store.disabled().get().length) !== 0; + showExport && items.push( { caption: 'Export All to File', handler: () => { From babaa210dbfb0002a696595bf53d846a60f886ee Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Thu, 27 Jul 2023 11:28:12 +0200 Subject: [PATCH 04/10] Fix TS linting issues --- application/client/src/app/service/history/provider.ts | 2 +- .../src/app/ui/views/sidebar/search/providers/providers.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/client/src/app/service/history/provider.ts b/application/client/src/app/service/history/provider.ts index 0ba030b193..36767fc4e4 100644 --- a/application/client/src/app/service/history/provider.ts +++ b/application/client/src/app/service/history/provider.ts @@ -83,7 +83,7 @@ export class Provider implements EntryConvertable { }); return col; }); - let uuid: string[] = this.collections.map(collection => collection.uuid); + const uuid: string[] = this.collections.map(collection => collection.uuid); this.storage.definitions.add(this.definitions); this.storage.collections.add(this.collections); this.collections = []; diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index 46d5ba4c18..baf9b7eb76 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -518,8 +518,8 @@ export class Providers { return items } items.push({}); - let store = this.session.search.store(); - let showExport: boolean = (store.filters().get().length + store.charts().get().length + store.disabled().get().length) !== 0; + const store = this.session.search.store(); + const showExport: boolean = (store.filters().get().length + store.charts().get().length + store.disabled().get().length) !== 0; showExport && items.push( { caption: 'Export All to File', From eb9298cc27599b664b9671ba049cf7bfca6ccce3 Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Thu, 27 Jul 2023 14:32:56 +0200 Subject: [PATCH 05/10] Refactor context menu functions --- .../sidebar/search/providers/providers.ts | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index baf9b7eb76..f6c668fefd 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -17,6 +17,7 @@ import { Logger } from '@platform/log'; import { ProvidersEvents } from './definitions/events'; import { history } from '@service/history'; import { bridge } from '@service/bridge'; +import { HistorySession } from '@service/history/session'; type TSelectedEntities = string[]; @@ -523,48 +524,54 @@ export class Providers { showExport && items.push( { caption: 'Export All to File', - handler: () => { - bridge.files().select.save().then((filename: string | undefined) => { - if(filename === undefined) { - return; - } - history.export([historySession.collections.uuid], filename); - }).catch(error => this.logger.error(error.message)); - }, + handler: () => this.exportFilters(historySession), }); items.push( { caption: 'Import from File', - handler: () => { - bridge.files().select.text() - .then(file => { - if (file.length !== 1) { - this.logger.error('No file selected'); - return; - } - history.import(file[0].filename).then((uuids: string[]) => { - if (uuids.length === 0) { - this.logger.warn('File does not have collection'); - return; - } - if (uuids.length > 1) { - this.session.switch().toolbar.presets(); - return; - } else { - const collection = history.collections.get(uuids[0]); - if (collection === undefined) { - this.logger.error(`Cannot find imported collection with UUID: ${uuids[0]}`); - return; - } - historySession.apply(collection); - } - }); - }); - }, + handler: () => this.importFilterFile(historySession), }); return items; } + private exportFilters = (historySession: HistorySession) => { + bridge.files().select.save() + .then((filename: string | undefined) => { + if (filename === undefined) + return; + history.export([historySession.collections.uuid], filename); + }) + .catch(error => this.logger.error(error.message)); + } + + private importFilterFile = (historySession: HistorySession) => { + bridge.files().select.text() + .then(file => { + if (file.length !== 1) { + this.logger.error('No file selected'); + return; + } + history.import(file[0].filename) + .then((uuids: string[]) => { + if (uuids.length === 0) { + this.logger.warn('File does not have a collection'); + return; + } + if (uuids.length > 1) { + this.session.switch().toolbar.presets(); + return; + } else { + const collection = history.collections.get(uuids[0]); + if (collection === undefined) { + this.logger.error(`Cannot find imported collection with UUID: ${uuids[0]}`); + return + } + historySession.apply(collection); + } + }) + }) + }; + private _onDoubleclickEvent(event: IDoubleclickEvent) { event.provider.search(event.entity); } From 0a80724b3ede6741a923a10985c18a46993dae49 Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Thu, 27 Jul 2023 14:51:05 +0200 Subject: [PATCH 06/10] Add import/export functionality for filter clicks --- .../views/sidebar/search/providers/providers.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index f6c668fefd..e32705c7ba 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -489,12 +489,7 @@ export class Providers { }, }); - event.items.push({ /* Delimiter */}, { - caption: 'Export All to File', - handler: () => console.log('Exporting to the file'), - }); - - event.items.push({ caption: 'Import from File', handler: () => console.log('Importing from file') }); + this.contextMenuOptions(event.items); this._providers.forEach((provider: Provider) => { const custom: IMenuItem[] = provider.getContextMenuItems( @@ -518,23 +513,23 @@ export class Providers { this.logger.error('History session is not defined'); return items } - items.push({}); + items.push({ /* Delimiter */ }); const store = this.session.search.store(); const showExport: boolean = (store.filters().get().length + store.charts().get().length + store.disabled().get().length) !== 0; showExport && items.push( { caption: 'Export All to File', - handler: () => this.exportFilters(historySession), + handler: () => this._exportFilters(historySession), }); items.push( { caption: 'Import from File', - handler: () => this.importFilterFile(historySession), + handler: () => this._importFilterFile(historySession), }); return items; } - private exportFilters = (historySession: HistorySession) => { + private _exportFilters = (historySession: HistorySession) => { bridge.files().select.save() .then((filename: string | undefined) => { if (filename === undefined) @@ -544,7 +539,7 @@ export class Providers { .catch(error => this.logger.error(error.message)); } - private importFilterFile = (historySession: HistorySession) => { + private _importFilterFile = (historySession: HistorySession) => { bridge.files().select.text() .then(file => { if (file.length !== 1) { From cd041d46b4b1d73a981f2c1aa552ea8b7edd2430 Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Fri, 28 Jul 2023 14:21:26 +0200 Subject: [PATCH 07/10] Fix PR comments --- .../sidebar/search/providers/providers.ts | 86 ++++++++++--------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index e32705c7ba..435b30a1a6 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -489,8 +489,6 @@ export class Providers { }, }); - this.contextMenuOptions(event.items); - this._providers.forEach((provider: Provider) => { const custom: IMenuItem[] = provider.getContextMenuItems( event.entity, @@ -503,6 +501,9 @@ export class Providers { event.items = event.items.concat(custom); } }); + + this.contextMenuOptions(event.items); + this.subjects.get().context.emit(event); } @@ -511,7 +512,7 @@ export class Providers { const historySession = history.get(this.session); if(historySession === undefined) { this.logger.error('History session is not defined'); - return items + return items; } items.push({ /* Delimiter */ }); const store = this.session.search.store(); @@ -519,53 +520,56 @@ export class Providers { showExport && items.push( { caption: 'Export All to File', - handler: () => this._exportFilters(historySession), + handler: () => this.filters(historySession).export(), }); items.push( { caption: 'Import from File', - handler: () => this._importFilterFile(historySession), + handler: () => this.filters(historySession).import(), }); return items; } - private _exportFilters = (historySession: HistorySession) => { - bridge.files().select.save() - .then((filename: string | undefined) => { - if (filename === undefined) - return; - history.export([historySession.collections.uuid], filename); - }) - .catch(error => this.logger.error(error.message)); - } - - private _importFilterFile = (historySession: HistorySession) => { - bridge.files().select.text() - .then(file => { - if (file.length !== 1) { - this.logger.error('No file selected'); - return; - } - history.import(file[0].filename) - .then((uuids: string[]) => { - if (uuids.length === 0) { - this.logger.warn('File does not have a collection'); - return; - } - if (uuids.length > 1) { - this.session.switch().toolbar.presets(); - return; - } else { - const collection = history.collections.get(uuids[0]); - if (collection === undefined) { - this.logger.error(`Cannot find imported collection with UUID: ${uuids[0]}`); - return + protected filters(historySession: HistorySession): { import(): void; export(): void } { + return { + import: (): void => { + bridge.files().select.text() + .then(file => { + if (file.length !== 1) { + this.logger.error('No file selected'); + return; } - historySession.apply(collection); - } - }) - }) - }; + history.import(file[0].filename) + .then((uuids: string[]) => { + if (uuids.length === 0) { + this.logger.warn('File does not have a collection'); + return; + } + if (uuids.length > 1) { + this.session.switch().toolbar.presets(); + return; + } else { + const collection = history.collections.get(uuids[0]); + if (collection === undefined) { + this.logger.error(`Cannot find imported collection with UUID: ${uuids[0]}`); + return; + } + historySession.apply(collection); + } + }) + }) + }, + export: (): void => { + bridge.files().select.save() + .then((filename: string | undefined) => { + if (filename === undefined) + return; + history.export([historySession.collections.uuid], filename); + }) + .catch(error => this.logger.error(error.message)); + } + }; + } private _onDoubleclickEvent(event: IDoubleclickEvent) { event.provider.search(event.entity); From 2a648cab7cded2028538059860fc9ff91cbc558f Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Fri, 28 Jul 2023 17:04:02 +0200 Subject: [PATCH 08/10] Add notification for import error --- .../sidebar/search/providers/providers.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index 435b30a1a6..7693dceb79 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -18,6 +18,7 @@ import { ProvidersEvents } from './definitions/events'; import { history } from '@service/history'; import { bridge } from '@service/bridge'; import { HistorySession } from '@service/history/session'; +import { Notification, notifications } from '@ui/service/notifications'; type TSelectedEntities = string[]; @@ -557,20 +558,34 @@ export class Providers { historySession.apply(collection); } }) + .catch(error => this._logAndNotifyError(error)); }) + .catch(error => this._logAndNotifyError(error)) }, export: (): void => { bridge.files().select.save() .then((filename: string | undefined) => { if (filename === undefined) return; - history.export([historySession.collections.uuid], filename); + history.export([historySession.collections.uuid], filename) + .catch(error => this._logAndNotifyError(error)) }) - .catch(error => this.logger.error(error.message)); + .catch(error => this._logAndNotifyError(error)) } }; } + private _logAndNotifyError(error: any): void { + this.logger.error(error.message); + notifications.notify( + new Notification({ + message: error.message, + session: this.session.uuid(), + actions: [] + }) + ); + } + private _onDoubleclickEvent(event: IDoubleclickEvent) { event.provider.search(event.entity); } From 81a401daced6e9cd1ea6596109dc091bcc868e57 Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Fri, 28 Jul 2023 17:23:47 +0200 Subject: [PATCH 09/10] Refactor error handling --- .../app/ui/views/sidebar/search/providers/providers.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index 7693dceb79..50dd3c92eb 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -558,9 +558,9 @@ export class Providers { historySession.apply(collection); } }) - .catch(error => this._logAndNotifyError(error)); + .catch(error => this.logAndNotifyError(error)); }) - .catch(error => this._logAndNotifyError(error)) + .catch(error => this.logAndNotifyError(error)) }, export: (): void => { bridge.files().select.save() @@ -568,14 +568,14 @@ export class Providers { if (filename === undefined) return; history.export([historySession.collections.uuid], filename) - .catch(error => this._logAndNotifyError(error)) + .catch(error => this.logAndNotifyError(error)) }) - .catch(error => this._logAndNotifyError(error)) + .catch(error => this.logAndNotifyError(error)) } }; } - private _logAndNotifyError(error: any): void { + protected logAndNotifyError(error: any): void { this.logger.error(error.message); notifications.notify( new Notification({ From d35e550b54044de07e08a673dea86d50e3596e8a Mon Sep 17 00:00:00 2001 From: sudeeptarlekar Date: Mon, 31 Jul 2023 11:01:43 +0200 Subject: [PATCH 10/10] Group common methods together --- .../sidebar/search/providers/providers.ts | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts index 50dd3c92eb..ef61c62e4e 100644 --- a/application/client/src/app/ui/views/sidebar/search/providers/providers.ts +++ b/application/client/src/app/ui/views/sidebar/search/providers/providers.ts @@ -503,19 +503,22 @@ export class Providers { } }); - this.contextMenuOptions(event.items); + this.injectGeneralMenuItems(event.items); this.subjects.get().context.emit(event); + } - public contextMenuOptions(items: IMenuItem[]): IMenuItem[] { + public injectGeneralMenuItems(items: IMenuItem[]): void { const historySession = history.get(this.session); if(historySession === undefined) { this.logger.error('History session is not defined'); - return items; + return; } - items.push({ /* Delimiter */ }); + items.push({ + /* Delimiter */ + }); const store = this.session.search.store(); const showExport: boolean = (store.filters().get().length + store.charts().get().length + store.disabled().get().length) !== 0; showExport && items.push( @@ -528,10 +531,20 @@ export class Providers { caption: 'Import from File', handler: () => this.filters(historySession).import(), }); - return items; } protected filters(historySession: HistorySession): { import(): void; export(): void } { + const logAndNotifyError = (error: Error): void => { + this.logger.error(error.message); + notifications.notify( + new Notification({ + message: error.message, + session: this.session.uuid(), + actions: [] + }) + ); + }; + return { import: (): void => { bridge.files().select.text() @@ -558,9 +571,9 @@ export class Providers { historySession.apply(collection); } }) - .catch(error => this.logAndNotifyError(error)); + .catch(error => logAndNotifyError(error)); }) - .catch(error => this.logAndNotifyError(error)) + .catch(error => logAndNotifyError(error)) }, export: (): void => { bridge.files().select.save() @@ -568,24 +581,13 @@ export class Providers { if (filename === undefined) return; history.export([historySession.collections.uuid], filename) - .catch(error => this.logAndNotifyError(error)) + .catch(error => logAndNotifyError(error)) }) - .catch(error => this.logAndNotifyError(error)) - } + .catch(error => logAndNotifyError(error)) + }, }; } - protected logAndNotifyError(error: any): void { - this.logger.error(error.message); - notifications.notify( - new Notification({ - message: error.message, - session: this.session.uuid(), - actions: [] - }) - ); - } - private _onDoubleclickEvent(event: IDoubleclickEvent) { event.provider.search(event.entity); }