Skip to content

Commit

Permalink
Multiple performance adjustments for app-users (#2563)
Browse files Browse the repository at this point in the history
(cherry picked from commit f8d02fe)
  • Loading branch information
reisfmb authored and alansemenov committed May 23, 2022
1 parent 5a5e53f commit 92012bf
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IdProviderKey} from './IdProviderKey';
import {Principal} from './Principal';
import {PrincipalKey} from './PrincipalKey';
import {GetPrincipalsByKeysRequest} from './GetPrincipalsByKeysRequest';
import {BaseLoader} from '../util/loader/BaseLoader';

export class PrincipalLoader
extends PostLoader<Principal> {
Expand Down Expand Up @@ -65,6 +66,11 @@ export class PrincipalLoader
return this.getRequest().isPartiallyLoaded();
}

setUseDataPreLoad(bool: boolean): PrincipalLoader {
super.setUseDataPreLoad(bool);
return this;
}

protected createRequest(): FindPrincipalsRequest {
return new FindPrincipalsRequest().setSize(10);
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/assets/admin/common/js/ui/grid/DataView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export class DataView<T extends Slick.SlickData> {
return this.slickDataView.getItems();
}

getItemsByIds(ids: string[]): T[] {
return ids.map(id => this.getItemById(id));
}

getItemById(id: string): T {
return this.slickDataView.getItemById(id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ export abstract class DropdownGrid<OPTION_DISPLAY_VALUE> {
});
}

getOptionsByValues(values: string[]): Option<OPTION_DISPLAY_VALUE>[] {
return this.getGridData().getItemsByIds(values);
}

getOptionByValue(value: string): Option<OPTION_DISPLAY_VALUE> {
return this.getGridData().getItemById(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export class DropdownList<OPTION_DISPLAY_VALUE> {
return this.dropdownGrid.getSelectedOptions();
}

getOptionsByValues(values: string[]): Option<OPTION_DISPLAY_VALUE>[] {
return this.dropdownGrid.getOptionsByValues(values);
}

getOptionByValue(value: string): Option<OPTION_DISPLAY_VALUE> {
return this.dropdownGrid.getOptionByValue(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ import * as Q from 'q';
import {BaseLoader} from '../../../util/loader/BaseLoader';
import {StringHelper} from '../../../util/StringHelper';
import {ComboBox, ComboBoxConfig} from './ComboBox';
import {LoadMask} from '../../mask/LoadMask';

export class BaseLoaderComboBox<OPTION_DISPLAY_VALUE, LOADER_DATA_TYPE>
extends ComboBox<OPTION_DISPLAY_VALUE> {

public static debug: boolean = false;
private loader: BaseLoader<LOADER_DATA_TYPE>;
private tempValue: string;
private mask: LoadMask;

constructor(name: string, config: ComboBoxConfig<OPTION_DISPLAY_VALUE>) {
super(name, config);

this.addClass('loader-combobox');

this.mask = new LoadMask(this);
}

public setLoader(loader: BaseLoader<LOADER_DATA_TYPE>) {
Expand Down Expand Up @@ -71,6 +75,7 @@ export class BaseLoaderComboBox<OPTION_DISPLAY_VALUE, LOADER_DATA_TYPE>
callback();
}
} else {
this.mask.show();
if (BaseLoaderComboBox.debug) {
console.debug(this.toString() + '.doWhenLoaded: waiting to be loaded');
}
Expand All @@ -88,7 +93,7 @@ export class BaseLoaderComboBox<OPTION_DISPLAY_VALUE, LOADER_DATA_TYPE>
this.loader.onLoadedData(singleLoadListener);

if (this.loader.isNotStarted()) {
this.loader.preLoad(value);
this.loader.preLoad(value).then(() => { this.mask.hide(); });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {SelectedOption} from './SelectedOption';
import {SelectedOptionEvent} from './SelectedOptionEvent';
import {BaseSelectedOptionView, BaseSelectedOptionViewBuilder} from './BaseSelectedOptionView';
import {assertNotNull} from '../../../util/Assert';
import {PEl} from '../../../dom/PEl';
import {i18n} from '../../../util/Messages';

export class BaseSelectedOptionsView<T>
extends DivEl
Expand All @@ -22,6 +24,7 @@ export class BaseSelectedOptionsView<T>
private optionAddedListeners: { (added: SelectedOptionEvent<T>): void; }[] = [];
private optionMovedListeners: { (moved: SelectedOption<T>, fromIndex: number): void }[] = [];
private editable: boolean = true;
static MAX_TO_APPEND: number = 100;

constructor(className?: string) {
super('selected-options' + (className ? ' ' + className : ''));
Expand Down Expand Up @@ -70,6 +73,37 @@ export class BaseSelectedOptionsView<T>
return new SelectedOption<T>(new BaseSelectedOptionView(builder), this.count());
}

/* Will mark all options as selected, but if there are more options than {MAX_TO_APPEND}
it'll append only {MAX_TO_APPEND} of them to the view in order to improve performance. */
addOptions(options: Option<T>[], silent: boolean = false, keyCode: number): boolean {
let result: boolean;

if (this.maximumOccurrencesReached()) { return false; }

if (options.length <= BaseSelectedOptionsView.MAX_TO_APPEND) {
result = options.every(option => this.addOption(option, silent, keyCode));
} else {

const selectedOptions: SelectedOption<T>[] = options.map((option, index) => {
const selectedOption = this.createSelectedOption(option);

if (index <= BaseSelectedOptionsView.MAX_TO_APPEND) {
const optionView = selectedOption.getOptionView();
optionView.onRemoveClicked(() => this.removeOption(option));
this.appendChild(optionView);
}

return selectedOption;
});

this.list = selectedOptions;

this.appendChild(new PEl('warning-truncated-users').setHtml(i18n('warning.optionsview.truncated')));
}

return result;
}

addOption(option: Option<T>, silent: boolean = false, keyCode: number): boolean {

if (this.isSelected(option) || this.maximumOccurrencesReached()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ export class ComboBox<OPTION_DISPLAY_VALUE>
return this.comboBoxDropdown.getOptions();
}

getOptionsByValues(values: string[]): Option<OPTION_DISPLAY_VALUE>[] {
return this.comboBoxDropdown.getOptionsByValues(values);
}

getOptionByValue(value: string): Option<OPTION_DISPLAY_VALUE> {
return this.comboBoxDropdown.getOptionByValue(value);
}
Expand Down Expand Up @@ -405,18 +409,7 @@ export class ComboBox<OPTION_DISPLAY_VALUE>
this.hideDropdown();
}

selectOption(option: Option<OPTION_DISPLAY_VALUE>, silent: boolean = false, keyCode: number = -1) {
assertNotNull(option, 'option cannot be null');
if (this.isOptionSelected(option)) {
return;
}

let added = this.selectedOptionsView.addOption(option, silent, keyCode);
if (!added) {
return;
}

this.comboBoxDropdown.markSelections(this.getSelectedOptions());
private selectOptionHelper(keyCode: number = -1) {
this.hideDropdown();
this.addClass('followed-by-options');

Expand All @@ -433,9 +426,38 @@ export class ComboBox<OPTION_DISPLAY_VALUE>
if (this.maximumOccurrencesReached() && this.hideComboBoxWhenMaxReached) {
this.hide();
}

this.ignoreNextFocus = false;
}

selectOptions(options: Option<OPTION_DISPLAY_VALUE>[], silent: boolean = false, keyCode: number = -1): void {
assertNotNull(options, 'options cannot be null');

const added = this.selectedOptionsView.addOptions(options, silent, keyCode);

if (!added) { return; }

this.comboBoxDropdown.markSelections(options);

this.selectOptionHelper(keyCode);
}

selectOption(option: Option<OPTION_DISPLAY_VALUE>, silent: boolean = false, keyCode: number = -1): void {
assertNotNull(option, 'option cannot be null');

if (this.isOptionSelected(option)) {
return;
}

const added = this.selectedOptionsView.addOption(option, silent, keyCode);

if (!added) { return; }

this.comboBoxDropdown.markSelections(this.getSelectedOptions());

this.selectOptionHelper(keyCode);
}

isOptionSelected(option: Option<OPTION_DISPLAY_VALUE>): boolean {
return this.selectedOptionsView.isSelected(option);
}
Expand Down Expand Up @@ -682,16 +704,16 @@ export class ComboBox<OPTION_DISPLAY_VALUE>
this.clearSelection(false, false, true);
}

let valueSetPromise;
let optionIds = this.splitValues(value);
let missingOptionIds = this.getMissingOptionsIds(optionIds);

if (this.displayMissingSelectedOptions || this.removeMissingSelectedOptions && missingOptionIds.length > 0) {
valueSetPromise = this.selectExistingAndHandleMissing(optionIds, missingOptionIds);
this.selectExistingAndHandleMissing(optionIds, missingOptionIds)
.then((options) => { this.notifyValueLoaded(options); });
} else {
valueSetPromise = Q(this.selectExistingOptions(optionIds));
const options = this.selectExistingOptions(optionIds);
this.notifyValueLoaded(options);
}
valueSetPromise.done((options) => this.notifyValueLoaded(options));
}

protected splitValues(value: string): string[] {
Expand Down Expand Up @@ -799,14 +821,9 @@ export class ComboBox<OPTION_DISPLAY_VALUE>

private selectExistingOptions(optionIds: string[]) {
let selectedOptions = [];

optionIds.forEach((val) => {
let option = this.getOptionByValue(val);
if (option != null) {
selectedOptions.push(option);
this.selectOption(option, true);
}
});
const options = this.getOptionsByValues(optionIds);
this.selectOptions(options);
selectedOptions = options;
return selectedOptions;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface SelectedOptionsView<T>

createSelectedOption(option: Option<T>): SelectedOption<T>;

addOptions(option: Option<T>[], silent: boolean, keyCode: number): boolean;

addOption(option: Option<T>, silent: boolean, keyCode: number): boolean;

updateOption(option: Option<T>, newOption: Option<T>, silent?: boolean);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export class BaseLoader<OBJECT> {

private comparator: Comparator<OBJECT>;

private useDataPreLoad: boolean = false;

constructor(request?: HttpRequest<OBJECT[]>) {
this.setRequest(request || this.createRequest());
}
Expand All @@ -50,7 +52,15 @@ export class BaseLoader<OBJECT> {
preLoad(searchString: string = ''): Q.Promise<OBJECT[]> {
this.notifyLoadingData(false);

return this.sendPreLoadRequest(searchString)
let promise: Q.Promise<OBJECT[]>;

if (this.useDataPreLoad) {
promise = this.preLoadData(searchString);
} else {
promise = this.sendPreLoadRequest(searchString);
}

return promise
.then<OBJECT[]>(this.handleLoadSuccess.bind(this, false))
.catch<OBJECT[]>(this.handleLoadError.bind(this, false))
.finally(() => this.status = LoaderStatus.PRE_LOADED);
Expand Down Expand Up @@ -173,6 +183,15 @@ export class BaseLoader<OBJECT> {
});
}

setUseDataPreLoad(bool: boolean): BaseLoader<OBJECT> {
this.useDataPreLoad = bool;
return this;
}

preLoadData(searchString: string): Q.Promise<OBJECT[]> {
throw new Error('Must be implemented in deriving classes!');
}

protected createRequest(): HttpRequest<OBJECT[]> {
throw new Error('Must be implemented in deriving classes!');
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/i18n/common.properties
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,8 @@ tooltip.filterPanel.show=Show Search Panel
tooltip.filterPanel.hide=Hide Search Panel
tooltip.header.expand=Click to expand
tooltip.header.collapse=Click to collapse

#
# Warnings
#
warning.optionsview.truncated = The list of users is truncated

0 comments on commit 92012bf

Please sign in to comment.