Skip to content

Commit 8aed648

Browse files
committed
feat: filters meta in session storage
1 parent f81d74f commit 8aed648

6 files changed

Lines changed: 238 additions & 153 deletions

File tree

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,13 @@
6969
"rc-trigger": "patches/rc-trigger.patch"
7070
},
7171
"onlyBuiltDependencies": [
72+
"@swc/core",
7273
"canvas"
74+
],
75+
"ignoredBuiltDependencies": [
76+
"@parcel/watcher",
77+
"core-js",
78+
"es5-ext"
7379
]
7480
},
7581
"prettier": "@mendix/prettier-config-web-widgets"

packages/pluggableWidgets/datagrid-web/src/controllers/DatasourceParamsController.ts

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { compactArray, fromCompactArray, isAnd } from "@mendix/filter-commons/condition-utils";
1+
import { reduceArray, restoreArray } from "@mendix/filter-commons/condition-utils";
22
import { QueryController } from "@mendix/widget-plugin-grid/query/query-controller";
3+
import { fnv1aHash } from "@mendix/widget-plugin-grid/utils/fnv-1a-hash";
34
import { disposeBatch } from "@mendix/widget-plugin-mobx-kit/disposeBatch";
45
import { ReactiveController, ReactiveControllerHost } from "@mendix/widget-plugin-mobx-kit/reactive-controller";
56
import { FilterCondition } from "mendix/filters";
6-
import { and } from "mendix/filters/builders";
77
import { makeAutoObservable, reaction } from "mobx";
88
import { SortInstruction } from "../typings/sorting";
99

@@ -22,10 +22,19 @@ type DatasourceParamsControllerSpec = {
2222
customFilters: FiltersInput;
2323
};
2424

25+
type FiltersMeta = {
26+
columnFilters: string;
27+
customFilters: string;
28+
combined: string;
29+
};
30+
31+
type CondArray = Array<FilterCondition | undefined>;
32+
2533
export class DatasourceParamsController implements ReactiveController {
2634
private columns: Columns;
2735
private query: QueryController;
2836
private customFilters: FiltersInput;
37+
readonly widgetName: string = "dataKey";
2938

3039
constructor(host: ReactiveControllerHost, spec: DatasourceParamsControllerSpec) {
3140
host.addController(this);
@@ -36,16 +45,41 @@ export class DatasourceParamsController implements ReactiveController {
3645
makeAutoObservable(this, { setup: false });
3746
}
3847

39-
private get derivedFilter(): FilterCondition | undefined {
40-
const { columns, customFilters } = this;
48+
private get derivedFilter(): {
49+
filter: FilterCondition | undefined;
50+
meta: FiltersMeta;
51+
hash: string | null;
52+
} {
53+
// return and(compactArray(this.columns.conditions), compactArray(this.customFilters.conditions));
54+
return this.reduceFilters(this.columns.conditions, this.customFilters.conditions);
55+
56+
// console.dir(...reduceArray(columns.conditions));
4157

42-
return and(compactArray(columns.conditions), compactArray(customFilters.conditions));
58+
// return [this.columns.conditions, this.customFilters.conditions];
4359
}
4460

4561
private get derivedSortOrder(): SortInstruction[] | undefined {
4662
return this.columns.sortInstructions;
4763
}
4864

65+
reduceFilters(
66+
columnFilters: CondArray,
67+
customFilters: CondArray
68+
): {
69+
filter: FilterCondition | undefined;
70+
meta: FiltersMeta;
71+
hash: string | null;
72+
} {
73+
const [columnsCond, columnsMeta] = reduceArray(columnFilters);
74+
const [customCond, customMeta] = reduceArray(customFilters);
75+
const [filter, combinedMeta] = reduceArray([columnsCond, customCond]);
76+
77+
const meta: FiltersMeta = { columnFilters: columnsMeta, customFilters: customMeta, combined: combinedMeta };
78+
const hash = filter ? DatasourceParamsController.filterHash(filter) : null;
79+
80+
return { filter, meta, hash };
81+
}
82+
4983
setup(): () => void {
5084
const [add, disposeAll] = disposeBatch();
5185
add(
@@ -58,28 +92,71 @@ export class DatasourceParamsController implements ReactiveController {
5892
add(
5993
reaction(
6094
() => this.derivedFilter,
61-
filter => this.query.setFilter(filter),
95+
(next, prev) => {
96+
if (prev && prev.hash) {
97+
this.clearFilterMeta(prev.hash);
98+
}
99+
if (next.hash) {
100+
this.saveFilterMeta(next.hash, next.meta);
101+
}
102+
this.query.setFilter(next.filter);
103+
},
62104
{ fireImmediately: true }
63105
)
64106
);
65107

66108
return disposeAll;
67109
}
68110

111+
storageKey(hash: string): string {
112+
return `${this.widgetName}:[${hash}]`;
113+
}
114+
115+
clearFilterMeta(hash: string): void {
116+
sessionStorage.removeItem(this.storageKey(hash));
117+
}
118+
119+
saveFilterMeta(hash: string | null, meta: FiltersMeta): void {
120+
if (!hash) {
121+
return;
122+
}
123+
124+
sessionStorage.setItem(this.storageKey(hash), JSON.stringify(meta));
125+
}
126+
127+
readFilterMeta(hash: string): FiltersMeta | null {
128+
const item = sessionStorage.getItem(this.storageKey(hash));
129+
if (!item) {
130+
return null;
131+
}
132+
try {
133+
return JSON.parse(item) as FiltersMeta;
134+
} catch (e) {
135+
console.error(`DatasourceParamsController.readFilterMeta: Error parsing meta for hash ${hash}`, e);
136+
return null;
137+
}
138+
}
139+
140+
static filterHash(filter: FilterCondition): string {
141+
return fnv1aHash(JSON.stringify(filter)).toString();
142+
}
143+
69144
static unzipFilter(
70-
filter?: FilterCondition
71-
): [columns: Array<FilterCondition | undefined>, sharedFilter: Array<FilterCondition | undefined>] {
145+
filter?: FilterCondition,
146+
widgetName = "dataKey"
147+
): [columnFilters: Array<FilterCondition | undefined>, customFilters: Array<FilterCondition | undefined>] {
72148
if (!filter) {
73149
return [[], []];
74150
}
75-
if (!isAnd(filter)) {
151+
const hash = this.filterHash(filter);
152+
const metaJson = sessionStorage.getItem(`${widgetName}:[${hash}]`);
153+
if (!metaJson) {
76154
return [[], []];
77155
}
78-
if (filter.args.length !== 2) {
79-
return [[], []];
80-
}
81-
82-
const [columns, shared] = filter.args;
83-
return [fromCompactArray(columns), fromCompactArray(shared)];
156+
const meta = JSON.parse(metaJson) as FiltersMeta;
157+
const [x, y] = restoreArray(filter, meta.combined);
158+
const columnFilters = restoreArray(x, meta.columnFilters);
159+
const customFilters = restoreArray(y, meta.customFilters);
160+
return [columnFilters, customFilters];
84161
}
85162
}
Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,6 @@
1-
/* eslint-disable no-bitwise */
1+
import { fnv1aHash } from "@mendix/widget-plugin-grid/utils/fnv-1a-hash";
22
import { GridColumn } from "../typings/GridColumn";
33

4-
/**
5-
* Generates 32 bit FNV-1a hash from the given string.
6-
* As explained here: http://isthe.com/chongo/tech/comp/fnv/
7-
*
8-
* @param s {string} String to generate hash from.
9-
* @param [h] {number} FNV-1a hash generation init value.
10-
* @returns {number} The result integer hash.
11-
*/
12-
function hash(s: string, h = 0x811c9dc5): number {
13-
const l = s.length;
14-
15-
for (let i = 0; i < l; i++) {
16-
h ^= s.charCodeAt(i);
17-
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
18-
}
19-
20-
return h >>> 0;
21-
}
22-
234
export function getHash(columns: GridColumn[], gridName: string): string {
245
const data = JSON.stringify({
256
name: gridName,
@@ -31,5 +12,5 @@ export function getHash(columns: GridColumn[], gridName: string): string {
3112
canDrag: col.canDrag
3213
}))
3314
});
34-
return hash(data).toString();
15+
return fnv1aHash(data).toString();
3516
}

packages/shared/filter-commons/src/condition-utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,43 @@ export function compactArray(input: Array<FilterCondition | undefined>): FilterC
105105
return and(metaTag, ...items);
106106
}
107107

108+
export function reduceArray(
109+
input: Array<FilterCondition | undefined>
110+
): [cond: FilterCondition | undefined, meta: string] {
111+
const [indexes, items] = shrink(input);
112+
const meta = JSON.stringify([input.length, indexes]);
113+
114+
switch (items.length) {
115+
case 0:
116+
return [undefined, meta];
117+
case 1:
118+
return [items[0], meta];
119+
default:
120+
return [and(...items), meta];
121+
}
122+
}
123+
124+
export function restoreArray(cond: FilterCondition | undefined, meta: string): Array<FilterCondition | undefined> {
125+
const [length, indexes] = JSON.parse(meta) as ArrayMeta;
126+
const arr: Array<FilterCondition | undefined> = Array(length).fill(undefined);
127+
128+
if (indexes.length === 0) {
129+
return arr;
130+
}
131+
if (indexes.length === 1) {
132+
arr[indexes[0]] = cond;
133+
return arr;
134+
}
135+
if (cond && isAnd(cond)) {
136+
cond.args.forEach((c, i) => {
137+
arr[indexes[i]] = c;
138+
});
139+
return arr;
140+
}
141+
142+
return arr;
143+
}
144+
108145
export function fromCompactArray(cond: FilterCondition): Array<FilterCondition | undefined> {
109146
const tag = isAnd(cond) ? cond.args[0] : cond;
110147

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* eslint-disable no-bitwise */
2+
3+
/**
4+
* Generates 32 bit FNV-1a hash from the given string.
5+
* As explained here: http://isthe.com/chongo/tech/comp/fnv/
6+
*
7+
* @param s {string} String to generate hash from.
8+
* @param [h] {number} FNV-1a hash generation init value.
9+
* @returns {number} The result integer hash.
10+
*/
11+
export function fnv1aHash(s: string, h: number = 0x811c9dc5): number {
12+
const l = s.length;
13+
14+
for (let i = 0; i < l; i++) {
15+
h ^= s.charCodeAt(i);
16+
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
17+
}
18+
19+
return h >>> 0;
20+
}

0 commit comments

Comments
 (0)